import { ref, computed, watch, nextTick, triggerRef } from 'vue'
import { useQuasar } from 'quasar'
import { useChatStore } from '../stores/chat'
import { getUser } from '../services/message'
import { logout, getUser as getCurrentUser } from '../services/auth'
import { getFriends as getImFriends, addFriend as addImFriend } from '../services/im-friendship'
import { getUserChats } from '../services/im-chat'
import { getGroup } from '../services/im-group'
import { compressImage, isImageFile, MessageContentType } from '../utils/image'
import { 
  normalizeGroupId,
  type User
} from './chat-window-utils'
import { useChatWindowCall } from './chat-window-call'
import { useChatWindowFriend } from './chat-window-friend'
import { useChatWindowGroup } from './chat-window-group'
import { useChatWindowUI } from './chat-window-ui'

// User 接口已移至 chat-window-utils.ts
export type { User } from './chat-window-utils'

export interface NewFriendForm {
  username: string
}


export const chatWindow = () => {
  const $q = useQuasar()
  const chatStore = useChatStore()

  // 状态
  const messagesContainerRef = ref<HTMLElement | null>(null)
  const fileInputRef = ref<HTMLInputElement | null>(null)
  const inputContentRef = ref<any>(null) // 输入框的 ref
  const inputContent = ref('')
  const sending = ref(false)
  const showUserDialog = ref(false)
  const showEmojiPicker = ref(false)
  const newFriendForm = ref<NewFriendForm>({ username: '' })
  const userList = ref<User[]>([])
  const friendList = ref<User[]>([])
  const recentChatUsers = ref<User[]>([])
  const selectedFiles = ref<File[]>([]) // 支持多张图片
  const uploading = ref(false)
  const searchingFriend = ref(false)
  const searchQuery = ref('')
  const leftMenuActive = ref('recent') // 'recent' | 'friends'
  const showSettings = ref(false)
  
  // 当前用户信息
  const currentUser = ref<{ name: string } | null>(null)

  // 计算属性
  const currentTargetId = computed(() => chatStore.currentTargetId)
  const targetType = computed(() => chatStore.targetType)
  const currentMessages = computed(() => chatStore.currentMessages)
  const connectionStatus = computed(() => chatStore.connectionStatus)
  const isConnected = computed(() => chatStore.isConnected)

  // 目标用户名称（支持异步加载）
  const targetNameRef = ref<string>('')
  
  // 异步加载目标用户信息
  const loadTargetUserInfo = async (targetId: string) => {
    if (!targetId) {
      targetNameRef.value = ''
      return
    }
    
    // 重要：优先使用 open_id 进行匹配，因为它是唯一不变的标识符
    // 首先在 userList 中查找（优先匹配 open_id，然后才是 id 和 snowflake_id）
    let user = userList.value.find(u => 
      u.open_id === targetId || // 优先匹配 open_id
      u.id === targetId || 
      (u.snowflake_id && String(u.snowflake_id) === targetId)
    )
    
    // 如果找不到，在 friendList 中查找（同样优先匹配 open_id）
    if (!user) {
      user = friendList.value.find(u => 
        u.open_id === targetId || // 优先匹配 open_id
        u.id === targetId || 
        (u.snowflake_id && String(u.snowflake_id) === targetId)
      )
    }
    
    // 如果找到了用户，返回用户名
    if (user && user.name) {
      targetNameRef.value = user.name
      return
    }
    
    // 如果找不到，尝试通过 API 获取用户信息
    try {
      const userInfo = await getUser(targetId)
      if (userInfo && userInfo.name) {
        targetNameRef.value = userInfo.name
        // 将获取到的用户信息添加到 userList 中
        // 重要：targetId 本身就是 open_id（从 chat.to_id 传入），所以优先使用 targetId
        // 如果 userInfo.open_id 存在且与 targetId 不同，使用 userInfo.open_id
        const openId = userInfo.open_id || targetId // 优先使用返回的 open_id，否则使用传入的 targetId
        const newUser: User = {
          id: openId, // 使用 open_id 作为 id
          name: userInfo.name,
          email: userInfo.email || targetId,
          snowflake_id: userInfo.snowflake_id ? String(userInfo.snowflake_id) : undefined,
          open_id: openId, // 确保 open_id 正确设置
        }
        // 检查是否已存在，避免重复添加（优先使用 open_id 匹配）
        if (!userList.value.find(u => u.open_id === openId || u.id === openId)) {
          userList.value.push(newUser)
        }
      } else {
        targetNameRef.value = targetId
      }
    } catch (error) {
      // 如果 API 调用失败，显示 ID
      targetNameRef.value = targetId
    }
  }

  // 群组列表缓存
  const groupsCache = ref<Map<string, { group_name: string; member_count?: number }>>(new Map())
  
  // 群成员信息缓存（用于快速查找群成员的alias和displayName）
  // key: groupId_memberId, value: { alias?: string, displayName?: string, name?: string }
  const groupMembersCache = ref<Map<string, { alias?: string; displayName?: string; name?: string }>>(new Map())

  // 验证群组名称是否有效
  const isValidGroupName = (name: string, groupId: string): boolean => {
    if (!name || name.trim() === '') return false
    if (name === groupId) return false
    // 检查是否是UUID格式
    const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
    if (uuidRegex.test(name)) return false
    // 检查是否是纯数字（可能是ID）
    if (/^\d+$/.test(name)) return false
    return true
  }

  // normalizeGroupId 已移至 chat-window-utils.ts
  
  // 从 recentChatUsers 或 groupsCache 中查找群组名称的辅助函数（用于后备）
  const findGroupName = (groupId: string): string | null => {
    if (!groupId) return null
    
    const normalizedTargetId = normalizeGroupId(groupId)
    
    // 从 recentChatUsers 中查找（支持多种ID格式）
    const chatInList = recentChatUsers.value.find(u => {
      if (!u.id) return false
      const normalizedUId = normalizeGroupId(u.id)
      return u.id === groupId || normalizedUId === normalizedTargetId
    })
    
    if (chatInList && chatInList.name && 
        chatInList.name !== groupId &&
        normalizeGroupId(chatInList.name) !== normalizedTargetId) {
      // 更新缓存
      if (!groupsCache.value.has(groupId)) {
        groupsCache.value.set(groupId, {
          group_name: chatInList.name,
          member_count: chatInList.member_count
        })
      }
      return chatInList.name
    }
    
    // 从 groupsCache 中查找（支持多种ID格式）
    let cachedGroup = groupsCache.value.get(groupId)
    if (!cachedGroup) {
      for (const [key, value] of groupsCache.value.entries()) {
        if (normalizeGroupId(key) === normalizedTargetId) {
          cachedGroup = value
          break
        }
      }
    }
    
    if (cachedGroup && cachedGroup.group_name && 
        cachedGroup.group_name !== groupId &&
        normalizeGroupId(cachedGroup.group_name) !== normalizedTargetId) {
      return cachedGroup.group_name
    }
    
    return null
  }

  // 异步加载群组信息（主要用于更新缓存，targetName computed 会直接从 recentChatUsers 获取）
  const loadTargetGroupInfo = async (groupId: string) => {
    if (!groupId) return
    
    // 如果缓存中已有有效名称，异步验证并更新缓存（不阻塞）
    if (groupsCache.value.has(groupId)) {
      const cachedName = groupsCache.value.get(groupId)!.group_name
      if (cachedName && isValidGroupName(cachedName, groupId)) {
        // 异步验证并更新缓存（如果API返回的名称不同）
        ;(async () => {
          try {
            const groupInfo = await getGroup(groupId)
            if (currentTargetId.value === groupId && targetType.value === 'Group') {
              if (groupInfo && groupInfo.group_name && isValidGroupName(groupInfo.group_name, groupId)) {
                const existingCache = groupsCache.value.get(groupId)
                groupsCache.value.set(groupId, {
                  group_name: groupInfo.group_name,
                  member_count: groupInfo.member_count || existingCache?.member_count
                })
              }
            }
          } catch (error: any) {
            // 静默处理错误
            if (error.message && !error.message.includes('404') && !error.message.includes('不存在')) {
              console.warn('验证群组信息失败:', groupId, error)
            }
          }
        })().catch(() => {})
        return
      }
    }
    
    // 从 API 获取群组信息并更新缓存
    try {
      const groupInfo = await getGroup(groupId)
      if (groupInfo && groupInfo.group_name && isValidGroupName(groupInfo.group_name, groupId)) {
        // 更新缓存
        groupsCache.value.set(groupId, { 
          group_name: groupInfo.group_name,
          member_count: groupInfo.member_count 
        })
      } else {
        // 如果 API 返回的名称无效，尝试从 recentChatUsers 获取
        const groupName = findGroupName(groupId)
        if (groupName) {
          // 更新缓存
          groupsCache.value.set(groupId, {
            group_name: groupName,
            member_count: undefined
          })
        }
      }
    } catch (error: any) {
      // 如果 API 调用失败，尝试从 recentChatUsers 获取
      if (error.message && !error.message.includes('404') && !error.message.includes('不存在')) {
        console.warn('获取群组信息失败:', groupId, error)
      }
      const groupName = findGroupName(groupId)
      if (groupName) {
        groupsCache.value.set(groupId, {
          group_name: groupName,
          member_count: undefined
        })
      }
    }
  }
  
  // 监听目标用户/群组变化，异步加载信息
  watch([currentTargetId, targetType], ([newId, newType]) => {
    try {
      if (newId && newType === 'User') {
        loadTargetUserInfo(newId).catch(err => {
          console.warn('加载用户信息失败:', err)
        })
      } else if (newId && newType === 'Group') {
        // 对于群组，targetName computed 会直接从 recentChatUsers 获取名称
        // 这里只需要异步加载群组信息来更新缓存（用于其他地方）
        loadTargetGroupInfo(newId).catch(err => {
          console.warn('加载群组信息失败:', err)
        })
      } else {
        targetNameRef.value = ''
      }
    } catch (error) {
      console.error('监听目标变化时出错:', error)
    }
  }, { immediate: true })
  
  const targetName = computed(() => {
    if (!currentTargetId.value) return ''
    
    // 判断是否为群组（不依赖 targetType，因为可能还没正确设置）
    const normalizedTargetId = normalizeGroupId(currentTargetId.value)
    const chatInList = recentChatUsers.value.find(u => {
      if (!u.id) return false
      const normalizedUId = normalizeGroupId(u.id)
      return u.id === currentTargetId.value || normalizedUId === normalizedTargetId
    })
    
    // 判断是否为群组：以 chat_type === 2 为主判断，人数只能作为辅助
    const isGroup = chatInList && (
      (chatInList as any)?.chat_type === 2 ||
      currentTargetId.value.startsWith('group_') ||
      ((chatInList as any)?.chat_type === undefined && 
       (chatInList as any)?.member_count !== undefined && 
       (chatInList as any).member_count >= 3)
    )
    
    // 如果是群组，优先从 recentChatUsers 中获取名称（后端返回的）
    if (isGroup) {
      if (chatInList && chatInList.name && 
          chatInList.name !== currentTargetId.value &&
          normalizeGroupId(chatInList.name) !== normalizedTargetId) {
        // 直接返回后端返回的群组名称
        return chatInList.name
      }
    }
    
    // 如果有 targetNameRef 的值且不是ID，直接返回
    if (targetNameRef.value && targetNameRef.value !== currentTargetId.value) {
      return targetNameRef.value
    }
    
    // 否则返回 ID（等待异步加载）
    return currentTargetId.value
  })

  // 获取用户首字母（需要在 targetName 定义之后，useChatWindowCall 之前）
  const getUserInitial = (userId: string | null): string => {
    if (!userId) return 'U'
    
    // 如果是群组聊天，需要区分群组ID和发送者openid
    if (targetType.value === 'Group') {
      // 如果传入的userId是当前群组ID，返回群组名称首字母
      if (userId === currentTargetId.value) {
        const cachedGroup = groupsCache.value.get(userId)
        if (cachedGroup) {
          return cachedGroup.group_name[0] || 'G'
        }
        // 如果缓存中没有，返回群组ID的第一个字符
        return userId[0] || 'G'
      }
      
      // 否则，这是群组消息中发送者的openid，需要查找用户信息
      // 查找群成员信息（不使用alias，只使用用户名）
      const groupId = currentTargetId.value
      if (groupId) {
        // 标准化groupId（去掉group_前缀，统一格式）
        const normalizedGroupId = groupId.replace(/^group_/i, '').trim().toLowerCase()
        // 尝试多种缓存键格式
        const cacheKeys = [
          `${normalizedGroupId}_${userId}`,
          `${groupId}_${userId}`,
          `group_${normalizedGroupId}_${userId}`,
        ]
        
        for (const cacheKey of cacheKeys) {
          const memberInfo = groupMembersCache.value.get(cacheKey)
          if (memberInfo) {
            // 只使用displayName或name，不使用alias
            const displayName = memberInfo.displayName || memberInfo.name
            if (displayName) {
              return displayName[0] || 'U'
            }
          }
        }
      }
      
      // 如果群成员缓存中没有，在 userList 中查找（优先匹配 open_id，然后才是 id 和 snowflake_id）
      let user = userList.value.find(u => 
        u.open_id === userId || // 优先匹配 open_id
        u.id === userId || 
        (u.snowflake_id && String(u.snowflake_id) === userId)
      )
      
      // 如果找不到，在 friendList 中查找（同样优先匹配 open_id）
      if (!user) {
        user = friendList.value.find(u => 
          u.open_id === userId || // 优先匹配 open_id
          u.id === userId || 
          (u.snowflake_id && String(u.snowflake_id) === userId)
        )
      }
      
      // 返回用户名首字母，如果找不到用户则返回'U'而不是openid的首字母
      return user?.name?.[0] || 'U'
    }
    
    // 单聊：查找用户信息
    // 重要：优先使用 open_id 进行匹配
    // 在 userList 中查找（优先匹配 open_id，然后才是 id 和 snowflake_id）
    let user = userList.value.find(u => 
      u.open_id === userId || // 优先匹配 open_id
      u.id === userId || 
      (u.snowflake_id && String(u.snowflake_id) === userId)
    )
    
    // 如果找不到，在 friendList 中查找（同样优先匹配 open_id）
    if (!user) {
      user = friendList.value.find(u => 
        u.open_id === userId || // 优先匹配 open_id
        u.id === userId || 
        (u.snowflake_id && String(u.snowflake_id) === userId)
      )
    }
    
    return user?.name?.[0] || 'U'
  }

  // 中间层显示的内容
  const middleContent = computed(() => {
    if (leftMenuActive.value === 'recent') {
      return recentChatUsers.value
    } else if (leftMenuActive.value === 'friends') {
      return friendList.value
    }
    return []
  })

  // 初始化通话模块（需要在 targetName 定义之后）
  const callModule = useChatWindowCall(
    chatStore,
    currentTargetId,
    targetType,
    targetName,
    getUserInitial
  )

  // 群组人数缓存（需要在模块初始化之前定义）
  const groupMemberCountCache = ref<Map<string, { count: number; timestamp: number }>>(new Map())
  const GROUP_MEMBER_COUNT_CACHE_DURATION = 60000 // 缓存1分钟
  const currentGroupMemberCount = ref<number | undefined>(undefined)
  
  // 计算当前群组的成员数
  const currentMemberCount = computed(() => {
    try {
      if (!currentTargetId.value) return undefined
      if (targetType.value !== 'Group') return undefined
      if (currentGroupMemberCount.value !== undefined) {
        return currentGroupMemberCount.value
      }
      const chat = findCurrentChat()
      return (chat as any)?.member_count ?? undefined
    } catch (error) {
      console.error('计算群组人数时出错:', error)
      return undefined
    }
  })
  
  // 辅助函数：查找当前聊天记录（避免重复代码）
  const findCurrentChat = () => {
    if (!currentTargetId.value) return null
    const normalizedTargetId = normalizeGroupId(currentTargetId.value)
    return recentChatUsers.value.find(u => {
      if (!u.id) return false
      const normalizedUId = normalizeGroupId(u.id)
      return u.id === currentTargetId.value || normalizedUId === normalizedTargetId
    }) || null
  }
  
  // 计算当前聊天的chat_id和chat_type
  const currentChatId = computed(() => {
    const chat = findCurrentChat()
    return (chat as any)?.chat_id || null
  })
  
  const currentChatType = computed(() => {
    const chat = findCurrentChat()
    return (chat as any)?.chat_type || null
  })
  
  const currentChatSequence = computed(() => {
    const chat = findCurrentChat()
    return (chat as any)?.sequence || null
  })
  
  const currentReadSequence = computed(() => {
    const chat = findCurrentChat()
    return (chat as any)?.read_sequence ?? null
  })

  // 注意：模块初始化将在函数定义之后进行，以避免使用前未声明的错误

  // 过滤中间层内容
  const filteredMiddleContent = computed(() => {
    if (!searchQuery.value) {
      return middleContent.value
    }
    const query = searchQuery.value.toLowerCase()
    return middleContent.value.filter(user =>
      user.name.toLowerCase().includes(query) ||
      user.email.toLowerCase().includes(query)
    )
  })

  // 工具函数（保留在 ChatWindow.ts 中，因为它们需要访问 chatStore 和本地状态）
  const formatTime = (timestamp: number): string => {
    // 确保时间戳是数字类型，如果是字符串则转换
    let ts = timestamp;
    if (typeof ts === 'string') {
      ts = parseInt(ts, 10);
    }
    
    // 如果时间戳小于 13 位（秒级），转换为毫秒级
    if (ts < 1000000000000) {
      ts = ts * 1000;
    }
    
    const date = new Date(ts)
    
    // 检查日期是否有效
    if (isNaN(date.getTime())) {
      console.warn('无效的时间戳:', timestamp);
      return '时间错误';
    }
    
    const now = new Date()
    const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
    const messageDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())

    if (messageDate.getTime() === today.getTime()) {
      return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
    } else {
      const diffDays = Math.floor((today.getTime() - messageDate.getTime()) / (1000 * 60 * 60 * 24))
      if (diffDays === 1) {
        return '昨天 ' + date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
      } else if (diffDays < 7) {
        return `${diffDays}天前 ${date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })}`
      } else {
        return date.toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' })
      }
    }
  }
  
  // 包装函数以使用正确的名称
  const formatTimeWrapper = formatTime

  const getLastMessagePreview = (userId: string, chatType?: number): string => {
    const allMessages = chatStore.messages
    const normalizedUserId = String(userId).trim()
    
    const lastMessage = allMessages
      .filter(msg => {
        // 重要：如果提供了 chat_type，只匹配相同类型的消息
        // chat_type: 1=单聊，2=群聊
        if (chatType !== undefined && chatType !== null) {
          const msgChatType = msg.chat_type
          // 如果消息的 chat_type 与目标 chat_type 不匹配，直接过滤掉
          if (msgChatType !== chatType) {
            return false
          }
        }
        
        // 对于群聊（chatType === 2），检查 to_user_id 是否匹配群组ID
        if (chatType === 2) {
          const msgToId = String(msg.to_user_id || '').trim()
          const normalizedMsgToId = normalizeGroupId(msgToId)
          const normalizedTargetId = normalizeGroupId(normalizedUserId)
          return msgToId === normalizedUserId || normalizedMsgToId === normalizedTargetId
        }
        
        // 对于单聊（chatType === 1 或未指定），使用原来的逻辑
        if (msg.isReceived) {
          // 接收的消息：检查 from_user_id 是否匹配
          const fromId = String(msg.from_user_id || '').trim()
          let matches = fromId === normalizedUserId
          
          // 如果精确匹配失败，尝试通过好友列表匹配
          if (!matches) {
            try {
              const friendsStr = localStorage.getItem('friends') || '[]'
              const friends = JSON.parse(friendsStr)
              const friendMatch = friends.find((f: any) => {
                const friend = f.user || f
                const friendOpenId = String(friend.open_id || '')
                const friendName = String(friend.name || '')
                const fromMatches = friendOpenId === fromId || friendName === fromId
                const targetMatches = friendOpenId === normalizedUserId || friendName === normalizedUserId
                return fromMatches && targetMatches
              })
              if (friendMatch) {
                matches = true
              }
            } catch (e) {
              // 解析失败，忽略
            }
          }
          
          return matches
        } else {
          // 发送的消息：检查 to_user_id 是否匹配
          const toId = String(msg.to_user_id || '').trim()
          let matches = toId === normalizedUserId
          
          // 如果精确匹配失败，尝试通过好友列表匹配
          if (!matches) {
            try {
              const friendsStr = localStorage.getItem('friends') || '[]'
              const friends = JSON.parse(friendsStr)
              const friendMatch = friends.find((f: any) => {
                const friend = f.user || f
                const friendOpenId = String(friend.open_id || '')
                const friendName = String(friend.name || '')
                const toMatches = friendOpenId === toId || friendName === toId
                const targetMatches = friendOpenId === normalizedUserId || friendName === normalizedUserId
                return toMatches && targetMatches
              })
              if (friendMatch) {
                matches = true
              }
            } catch (e) {
              // 解析失败，忽略
            }
          }
          
          // 如果还是不匹配，且消息是当前用户发送的，检查格式不一致的情况
          if (!matches) {
            const toIdIsUsername = !/^\d+$/.test(toId) && !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(toId)
            const userIdIsOpenId = /^\d+$/.test(normalizedUserId) || /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(normalizedUserId)
            const toIdIsOpenId = /^\d+$/.test(toId) || /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(toId)
            const userIdIsUsername = !/^\d+$/.test(normalizedUserId) && !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(normalizedUserId)
            
            // 如果格式不一致（一个用户名，一个open_id），可能是同一个用户
            if ((toIdIsUsername && userIdIsOpenId) || (toIdIsOpenId && userIdIsUsername)) {
              matches = true
            }
          }
          
          return matches
        }
      })
      .sort((a, b) => {
        // 使用 timestamp_ms 如果存在，否则使用 timestamp
        const aTime = a.timestamp_ms || a.timestamp
        const bTime = b.timestamp_ms || b.timestamp
        return bTime - aTime
      })[0]
    
    if (!lastMessage) {
      // 如果本地没有消息，检查是否有未读消息（说明后端有消息，只是还没加载）
      const chatInList = recentChatUsers.value.find(u => {
        if (!u.id) return false
        const normalizedUId = normalizeGroupId(u.id)
        const normalizedTargetId = normalizeGroupId(normalizedUserId)
        return u.id === normalizedUserId || normalizedUId === normalizedTargetId
      })
      
      // 如果有未读消息（sequence > read_sequence），说明后端有消息，不应该显示"暂无消息"
      if (chatInList && (chatInList as any).sequence && (chatInList as any).read_sequence !== undefined) {
        const sequence = (chatInList as any).sequence
        const readSequence = (chatInList as any).read_sequence
        if (sequence > readSequence) {
          // 有未读消息，返回空字符串或提示（不显示"暂无消息"）
          return ''
        }
      }
      
      return '暂无消息'
    }
    
    if (lastMessage.file_name) {
      return `[文件] ${lastMessage.file_name}`
    }
    
    const text = lastMessage.message.replace(/[#*_`\[\]()]/g, '').trim()
    return text.length > 20 ? text.substring(0, 20) + '...' : text
  }

  const getLastMessageTime = (userId: string, chatType?: number): string => {
    const allMessages = chatStore.messages
    const normalizedUserId = String(userId).trim()
    
    // 使用与 getLastMessagePreview 相同的匹配逻辑
    const lastMessage = allMessages
      .filter(msg => {
        // 重要：如果提供了 chat_type，只匹配相同类型的消息
        // chat_type: 1=单聊，2=群聊
        if (chatType !== undefined && chatType !== null) {
          const msgChatType = msg.chat_type
          // 如果消息的 chat_type 与目标 chat_type 不匹配，直接过滤掉
          if (msgChatType !== chatType) {
            return false
          }
        }
        
        // 对于群聊（chatType === 2），检查 to_user_id 是否匹配群组ID
        if (chatType === 2) {
          const msgToId = String(msg.to_user_id || '').trim()
          const normalizedMsgToId = normalizeGroupId(msgToId)
          const normalizedTargetId = normalizeGroupId(normalizedUserId)
          return msgToId === normalizedUserId || normalizedMsgToId === normalizedTargetId
        }
        
        // 对于单聊（chatType === 1 或未指定），使用原来的逻辑
        if (msg.isReceived) {
          const fromId = String(msg.from_user_id || '').trim()
          let matches = fromId === normalizedUserId
          
          if (!matches) {
            try {
              const friendsStr = localStorage.getItem('friends') || '[]'
              const friends = JSON.parse(friendsStr)
              const friendMatch = friends.find((f: any) => {
                const friend = f.user || f
                const friendOpenId = String(friend.open_id || '')
                const friendName = String(friend.name || '')
                const fromMatches = friendOpenId === fromId || friendName === fromId
                const targetMatches = friendOpenId === normalizedUserId || friendName === normalizedUserId
                return fromMatches && targetMatches
              })
              if (friendMatch) {
                matches = true
              }
            } catch (e) {
              // 解析失败，忽略
            }
          }
          
          return matches
        } else {
          const toId = String(msg.to_user_id || '').trim()
          let matches = toId === normalizedUserId
          
          if (!matches) {
            try {
              const friendsStr = localStorage.getItem('friends') || '[]'
              const friends = JSON.parse(friendsStr)
              const friendMatch = friends.find((f: any) => {
                const friend = f.user || f
                const friendOpenId = String(friend.open_id || '')
                const friendName = String(friend.name || '')
                const toMatches = friendOpenId === toId || friendName === toId
                const targetMatches = friendOpenId === normalizedUserId || friendName === normalizedUserId
                return toMatches && targetMatches
              })
              if (friendMatch) {
                matches = true
              }
            } catch (e) {
              // 解析失败，忽略
            }
          }
          
          // 如果还是不匹配，检查格式不一致的情况
          if (!matches) {
            const toIdIsUsername = !/^\d+$/.test(toId) && !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(toId)
            const userIdIsOpenId = /^\d+$/.test(normalizedUserId) || /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(normalizedUserId)
            const toIdIsOpenId = /^\d+$/.test(toId) || /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(toId)
            const userIdIsUsername = !/^\d+$/.test(normalizedUserId) && !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(normalizedUserId)
            
            if ((toIdIsUsername && userIdIsOpenId) || (toIdIsOpenId && userIdIsUsername)) {
              matches = true
            }
          }
          
          return matches
        }
      })
      .sort((a, b) => {
        // 使用 timestamp_ms 如果存在，否则使用 timestamp
        const aTime = a.timestamp_ms || a.timestamp
        const bTime = b.timestamp_ms || b.timestamp
        return bTime - aTime
      })[0]
    
    if (!lastMessage) return ''
    // 使用 timestamp_ms 如果存在，否则使用 timestamp
    const timestamp = lastMessage.timestamp_ms || lastMessage.timestamp
    return formatTime(timestamp)
  }
  
  const getUserName = (userId: string | null): string => {
    if (!userId) return '未知用户'
    
    // 如果是群组聊天，需要区分群组ID和发送者openid
    if (targetType.value === 'Group') {
      // 如果传入的userId是当前群组ID，返回群组名称
      if (userId === currentTargetId.value) {
        const cachedGroup = groupsCache.value.get(userId)
        if (cachedGroup) {
          return cachedGroup.group_name
        }
        return userId
      }
      
      // 否则，这是群组消息中发送者的openid，需要查找用户信息
      // 查找群成员信息（不使用alias，只使用用户名）
      const groupId = currentTargetId.value
      if (groupId) {
        // 标准化groupId（去掉group_前缀，统一格式）
        const normalizedGroupId = groupId.replace(/^group_/i, '').trim().toLowerCase()
        // 尝试多种缓存键格式
        const cacheKeys = [
          `${normalizedGroupId}_${userId}`,
          `${groupId}_${userId}`,
          `group_${normalizedGroupId}_${userId}`,
        ]
        
        for (const cacheKey of cacheKeys) {
          const memberInfo = groupMembersCache.value.get(cacheKey)
          if (memberInfo) {
            // 只使用displayName或name，不使用alias
            const displayName = memberInfo.displayName || memberInfo.name
            if (displayName) {
              return displayName
            }
          }
        }
      }
      
      // 如果群成员缓存中没有，在 userList 中查找（优先匹配 open_id，然后才是 id 和 snowflake_id）
      let user = userList.value.find(u => 
        u.open_id === userId || // 优先匹配 open_id
        u.id === userId || 
        (u.snowflake_id && String(u.snowflake_id) === userId)
      )
      
      // 如果找不到，在 friendList 中查找（同样优先匹配 open_id）
      if (!user) {
        user = friendList.value.find(u => 
          u.open_id === userId || // 优先匹配 open_id
          u.id === userId || 
          (u.snowflake_id && String(u.snowflake_id) === userId)
        )
      }
      
      // 返回用户名，如果找不到用户则返回userId
      return user?.name || userId
    }
    
    // 单聊：查找用户信息
    // 重要：优先使用 open_id 进行匹配
    // 在 userList 中查找（优先匹配 open_id，然后才是 id 和 snowflake_id）
    let user = userList.value.find(u => 
      u.open_id === userId || // 优先匹配 open_id
      u.id === userId || 
      (u.snowflake_id && String(u.snowflake_id) === userId)
    )
    
    // 如果找不到，在 friendList 中查找（同样优先匹配 open_id）
    if (!user) {
      user = friendList.value.find(u => 
        u.open_id === userId || // 优先匹配 open_id
        u.id === userId || 
        (u.snowflake_id && String(u.snowflake_id) === userId)
      )
    }
    
    return user?.name || userId
  }

  // 滚动到底部
  const scrollToBottom = () => {
    if (messagesContainerRef.value) {
      nextTick(() => {
        if (messagesContainerRef.value) {
          const container = messagesContainerRef.value
          // 使用 requestAnimationFrame 确保在下一帧渲染时滚动
          requestAnimationFrame(() => {
            container.scrollTop = container.scrollHeight
            // 再次确保滚动到底部
            setTimeout(() => {
              container.scrollTop = container.scrollHeight
            }, 50)
          })
        }
      })
    }
  }

  // 加载好友列表
  const loadFriends = async () => {
    try {
      const friends = await getImFriends()
      // 将 IM 好友格式转换为 User 格式
      // 重要：使用 open_id 作为唯一标识符，因为 user id 和 username 可能会变化
      friendList.value = friends.map(f => {
        // to_id 就是 open_id（后端使用 get_external_id() 返回的值）
        // 优先使用 user.open_id，如果没有则使用 to_id（to_id 本身就是 open_id）
        const openId = f.user?.open_id || f.to_id
        // 优先使用备注，然后是用户名，最后才是 open_id
        const userName = f.remark || f.user?.name || openId
        
        return {
          id: openId, // 使用 open_id 作为唯一标识符
          name: userName,
          email: f.user?.email || openId,
          open_id: openId, // 明确设置 open_id
          snowflake_id: f.user?.snowflake_id ? String(f.user.snowflake_id) : undefined,
        }
      })
      // 合并 userList 和 friendList，使用 open_id 作为唯一标识符
      const allOpenIds = new Set([
        ...userList.value.map(u => u.open_id || u.id).filter(Boolean),
        ...friendList.value.map(u => u.open_id || u.id).filter(Boolean)
      ])
      userList.value = Array.from(allOpenIds).map(openId => {
        // 优先从 friendList 中查找（因为好友信息更完整），然后才是 userList
        return friendList.value.find(u => (u.open_id || u.id) === openId) || 
               userList.value.find(u => (u.open_id || u.id) === openId)!
      })
    } catch (error: any) {
      console.error('加载好友列表失败:', error)
    }
  }

  // 更新最近聊天列表（从后端 API 获取，如果失败则从本地消息提取）
  const updateRecentChatUsers = async () => {
    // 标准化群组ID的辅助函数（在整个函数中使用）
    const standardizeGroupId = (id: string) => id.startsWith('group_') ? id : `group_${id}`
    
    try {
      // 从后端 API 获取聊天列表
      const chats = await getUserChats()
      
      // 分离群组和用户聊天
      const groupChats = chats.filter(chat => chat.chat_type === 2)
      const userChats = chats.filter(chat => chat.chat_type === 1)
      
      // 获取所有需要查询的用户ID
      const userIds = new Set<string>()
      userChats.forEach(chat => {
        if (chat.to_id) {
          userIds.add(chat.to_id)
        }
      })
      
      // 获取所有需要查询的群组ID（统一标准化为带 group_ 前缀的格式）
      const groupIds = new Set<string>()
      
      groupChats.forEach(chat => {
        if (chat.to_id) {
          // 标准化ID格式（统一为带 group_ 前缀）
          const standardizedId = standardizeGroupId(chat.to_id)
          groupIds.add(standardizedId)
        }
      })
      
      // 批量获取用户信息
      const { getUser } = await import('../services/message')
      const userInfoMap = new Map<string, User>()
      
      await Promise.all(Array.from(userIds).map(async (userId) => {
        try {
          // 首先在现有的 userList 和 friendList 中查找
          let user = userList.value.find(u => 
            u.open_id === userId || 
            u.id === userId || 
            (u.snowflake_id && String(u.snowflake_id) === userId)
          )
          
          if (!user) {
            user = friendList.value.find(u => 
              u.open_id === userId || 
              u.id === userId || 
              (u.snowflake_id && String(u.snowflake_id) === userId)
            )
          }
          
          // 如果找不到，通过 API 获取
          if (!user) {
            try {
              const userInfo = await getUser(userId)
              if (userInfo) {
                // 重要：chat.to_id 是 open_id，所以 userId 就是 open_id
                // 但 getUser 返回的 userInfo.id 可能是 snowflake_id 或数据库 id
                // 所以我们需要确保 open_id 字段正确设置
                const openId = userInfo.open_id || userId // 优先使用返回的 open_id，否则使用传入的 userId（即 chat.to_id）
                user = {
                  id: openId, // 使用 open_id 作为 id
                  name: userInfo.name,
                  email: userInfo.email || openId,
                  snowflake_id: userInfo.snowflake_id ? String(userInfo.snowflake_id) : undefined,
                  open_id: openId, // 确保 open_id 正确设置
                }
                // 添加到 userList（如果不存在，使用 open_id 匹配）
                if (!userList.value.find(u => u.open_id === openId || u.id === openId)) {
                  userList.value.push(user)
                }
              }
            } catch (e: any) {
              // 静默处理 404 错误（用户不存在），不打印日志
            }
          }
          
          if (user) {
            // 使用传入的 userId（即 chat.to_id，应该是 open_id）作为键
            userInfoMap.set(userId, user)
            // 同时建立 open_id 到用户的映射（如果 open_id 与 userId 不同）
            if (user.open_id && user.open_id !== userId) {
              userInfoMap.set(user.open_id, user)
            }
            // 如果 user.id 与 userId 不同，也建立映射
            if (user.id && user.id !== userId && user.id !== user.open_id) {
              userInfoMap.set(user.id, user)
            }
          }
        } catch (e: any) {
          // 转换失败，创建临时用户对象
          // 静默处理 404 错误（用户不存在），不打印日志
          const tempUser: User = {
            id: userId,
            name: userId,
            email: userId,
            open_id: userId,
          }
          userInfoMap.set(userId, tempUser)
        }
      }))
      
      // 批量获取群组信息
      // 注意：不再请求群组列表，只按需获取单个群组信息（群组从聊天记录中自动识别）
      const { getGroup } = await import('../services/im-group')
      const groupInfoMap = new Map<string, { id: string; name: string; member_count?: number }>()
      
      // 加载群组列表（先从服务器获取最新列表，用于验证群组是否存在）
      const { loadGroupsFromCache, getUserGroups } = await import('../services/im-group')
      
      // 先初始化 groupsCache（从本地缓存加载）
      const cachedGroups = await loadGroupsFromCache()
      if (cachedGroups && cachedGroups.length > 0) {
        cachedGroups.forEach(group => {
          groupsCache.value.set(group.group_id, {
            group_name: group.group_name,
            member_count: group.member_count
          })
        })
      }
      
      // 先尝试从服务器获取最新的群组列表（这是用户实际所在的群组）
      let serverGroups: any[] = []
      try {
        const groupsResult = await getUserGroups(false) // 不使用缓存，直接从服务器获取
        serverGroups = groupsResult.groups || []
      } catch (e) {
        console.warn('从服务器获取群组列表失败，使用缓存:', e)
        // 如果服务器获取失败，使用缓存
        if (!cachedGroups) {
          const fallbackCachedGroups = await loadGroupsFromCache()
          serverGroups = fallbackCachedGroups || []
        } else {
          serverGroups = cachedGroups
        }
      }
      
      const allGroupsMap = new Map<string, { group_name: string; member_count?: number }>()
      if (serverGroups && serverGroups.length > 0) {
        serverGroups.forEach(group => {
          allGroupsMap.set(group.group_id, {
            group_name: group.group_name,
            member_count: group.member_count
          })
          // 同时更新 groupsCache（确保 groupsCache 包含最新的服务器数据）
          groupsCache.value.set(group.group_id, {
            group_name: group.group_name,
            member_count: group.member_count
          })
        })
      }
      
      await Promise.all(Array.from(groupIds).map(async (groupId) => {
        try {
          // 判断是否为UUID格式（用于检查名称是否有效）
          const isUUID = (str: string): boolean => {
            const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
            return uuidRegex.test(str)
          }
          
          // 先检查缓存
          if (groupsCache.value.has(groupId)) {
            const cachedGroup = groupsCache.value.get(groupId)!
            // 如果缓存中的名称是有效的（不是UUID，不是空，不等于ID），使用缓存
            if (cachedGroup.group_name && 
                cachedGroup.group_name !== groupId && 
                cachedGroup.group_name.trim() !== '' && 
                !isUUID(cachedGroup.group_name)) {
              groupInfoMap.set(groupId, {
                id: groupId,
                name: cachedGroup.group_name,
                member_count: cachedGroup.member_count, // 包含群人数
              })
              // 如果缓存中没有人数，尝试从API获取更新
              if (cachedGroup.member_count === undefined) {
                try {
                  // API需要不带 group_ 前缀的ID
                  const apiGroupId = normalizeGroupId(groupId)
                  const groupInfo = await getGroup(apiGroupId)
                  if (groupInfo && groupInfo.member_count !== undefined) {
                    // 更新缓存和映射（使用标准化的ID作为key）
                    groupsCache.value.set(groupId, {
                      group_name: cachedGroup.group_name,
                      member_count: groupInfo.member_count
                    })
                    groupInfoMap.set(groupId, {
                      id: groupId,
                      name: cachedGroup.group_name,
                      member_count: groupInfo.member_count,
                    })
                  }
                } catch (e: any) {
                  // 如果获取失败（可能是404），继续使用缓存（没有人数）
                  // 不记录为错误，因为群组可能已被删除
                  if (e.message && !e.message.includes('404') && !e.message.includes('不存在')) {
                    console.warn('无法更新群组人数:', groupId, e)
                  }
                }
              }
              return
            } else {
              // 如果缓存中的名称无效，继续尝试从API获取
            }
          }
          
          // 如果缓存中没有有效名称，先检查是否在服务器返回的群组列表中
          // 如果不在列表中，说明用户已经不是成员，不需要获取详细信息
          if (allGroupsMap.has(groupId)) {
            // 在服务器列表中，使用服务器返回的信息
            const groupFromServer = allGroupsMap.get(groupId)!
            groupInfoMap.set(groupId, {
              id: groupId,
              name: groupFromServer.group_name,
              member_count: groupFromServer.member_count,
            })
            // 更新缓存
            groupsCache.value.set(groupId, {
              group_name: groupFromServer.group_name,
              member_count: groupFromServer.member_count
            })
            return
          }
          
          // 如果不在服务器列表中，但聊天记录存在，说明可能是2人群组（不会出现在群组列表中）
          // 或者刚创建的群组，尝试从API获取群组信息
          try {
            // API需要不带 group_ 前缀的ID
            const apiGroupId = normalizeGroupId(groupId)
            const groupInfo = await getGroup(apiGroupId)
            if (groupInfo) {
              groupInfoMap.set(groupId, {
                id: groupId,
                name: groupInfo.group_name,
                member_count: groupInfo.member_count,
              })
              // 更新缓存（使用标准化的ID作为key）
              groupsCache.value.set(groupId, {
                group_name: groupInfo.group_name,
                member_count: groupInfo.member_count
              })
              return
            }
          } catch (e: any) {
            // 如果获取失败（可能是404），使用默认值
            // 静默处理404错误，不打印日志
          }
          
          // 如果API获取失败，仍然设置默认值，确保聊天记录能显示
          groupInfoMap.set(groupId, {
            id: groupId,
            name: groupId, // 使用 groupId 作为临时名称
            member_count: undefined,
          })
        } catch (e) {
          // 获取失败，检查缓存是否有有效名称
          if (groupsCache.value.has(groupId)) {
            const cachedGroup = groupsCache.value.get(groupId)!
            if (cachedGroup.group_name && cachedGroup.group_name !== groupId) {
              groupInfoMap.set(groupId, {
                id: groupId,
                name: cachedGroup.group_name,
                member_count: cachedGroup.member_count, // 包含群人数
              })
            } else {
              groupInfoMap.set(groupId, {
                id: groupId,
                name: groupId,
              })
            }
          } else {
            groupInfoMap.set(groupId, {
              id: groupId,
              name: groupId,
            })
          }
        }
      }))
      
      // 合并聊天列表和用户/群组信息
      // 先验证群组是否存在（过滤掉已删除的群组）
      const validGroupIds = new Set<string>()
      
      // 第一轮：快速验证（从已知的群组列表中）
      // 注意：normalizeGroupId 已经在函数开头定义
      
      for (const groupId of groupIds) {
        const normalizedGroupId = normalizeGroupId(groupId)
        let found = false
        
        // 如果 allGroupsMap 中有这个群组，说明群组存在
        // 需要同时检查原始ID和标准化ID
        for (const [mapId, info] of allGroupsMap.entries()) {
          const normalizedMapId = normalizeGroupId(mapId)
          if (mapId === groupId || normalizedMapId === normalizedGroupId) {
            validGroupIds.add(groupId)
            found = true
            break
          }
        }
        if (found) continue
        
        // 如果 groupInfoMap 中有有效的群组信息（名称不是ID），说明群组存在
        for (const [mapId, groupInfo] of groupInfoMap.entries()) {
          const normalizedMapId = normalizeGroupId(mapId)
          if ((mapId === groupId || normalizedMapId === normalizedGroupId) && 
              groupInfo && groupInfo.name && groupInfo.name !== groupId) {
            validGroupIds.add(groupId)
            found = true
            break
          }
        }
        if (found) continue
        
        // 如果缓存中有有效的群组信息，也认为群组存在（可能是刚创建的群组，还没同步到服务器列表）
        for (const [cacheId, cachedGroup] of groupsCache.value.entries()) {
          const normalizedCacheId = normalizeGroupId(cacheId)
          if (cacheId === groupId || normalizedCacheId === normalizedGroupId) {
            if (cachedGroup.group_name && 
                cachedGroup.group_name !== groupId && 
                cachedGroup.group_name.trim() !== '' &&
                !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(cachedGroup.group_name)) {
              validGroupIds.add(groupId)
              // 同时更新 groupInfoMap，确保后续能使用
              groupInfoMap.set(groupId, {
                id: groupId,
                name: cachedGroup.group_name,
                member_count: cachedGroup.member_count,
              })
              found = true
              break
            }
          }
        }
        if (found) continue
      }
      
      // 第二轮：验证其他群组（只验证在缓存中有名称但不在服务器列表中的群组）
      // 注意：如果群组不在服务器返回的列表中，说明用户已经不是成员，不需要验证
      const groupsToVerify = Array.from(groupIds).filter(groupId => {
        // 如果已经在有效列表中，跳过
        if (validGroupIds.has(groupId)) return false
        // 如果群组不在服务器列表中，但缓存中有有效名称，可能需要验证（可能是新加入的群组）
        // 但为了减少404错误，我们只验证缓存中有有效名称的群组
        if (groupsCache.value.has(groupId)) {
          const cachedGroup = groupsCache.value.get(groupId)!
          // 如果缓存中有有效名称，可能需要验证
          if (cachedGroup.group_name && 
              cachedGroup.group_name !== groupId && 
              cachedGroup.group_name.trim() !== '' &&
              !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(cachedGroup.group_name)) {
            return true
          }
        }
        // 其他情况不验证，避免404错误
        return false
      })
      
      // 并行验证这些群组（只验证缓存中有有效名称的群组）
      if (groupsToVerify.length > 0) {
        const { getGroup } = await import('../services/im-group')
        await Promise.all(groupsToVerify.map(async (groupId) => {
          try {
            const groupInfo = await getGroup(groupId)
            // 如果群组存在且有有效的名称，添加到有效列表
            if (groupInfo && groupInfo.group_name && groupInfo.group_name !== groupId) {
              validGroupIds.add(groupId)
              // 更新缓存
              groupsCache.value.set(groupId, {
                group_name: groupInfo.group_name,
                member_count: groupInfo.member_count
              })
            }
          } catch (e: any) {
            // 如果 getGroup 返回 404，说明群组不存在，静默过滤（不输出警告）
            if (e.message && (e.message.includes('404') || e.message.includes('不存在'))) {
              // 静默处理，不输出日志（避免控制台噪音）
              // 从缓存中移除无效的群组信息
              groupsCache.value.delete(groupId)
            } else {
              // 只有非404错误才输出警告
              console.warn('验证群组时出错:', groupId, e.message || e)
            }
          }
        }))
      }
      
      // 先处理需要从API获取的群组（不在有效列表中的群组）
      const groupsToFetchFromAPI = chats
        .filter(chat => chat.chat_type === 2 && !validGroupIds.has(chat.to_id) && !groupsCache.value.has(chat.to_id))
        .map(chat => chat.to_id)
      
      if (groupsToFetchFromAPI.length > 0) {
        const { getGroup } = await import('../services/im-group')
        await Promise.all(groupsToFetchFromAPI.map(async (groupId) => {
          try {
            const groupInfo = await getGroup(groupId)
            if (groupInfo && groupInfo.group_name && groupInfo.group_name !== groupId) {
              validGroupIds.add(groupId)
              groupInfoMap.set(groupId, {
                id: groupId,
                name: groupInfo.group_name,
                member_count: groupInfo.member_count,
              })
              // 更新缓存
              groupsCache.value.set(groupId, {
                group_name: groupInfo.group_name,
                member_count: groupInfo.member_count,
              })
            }
          } catch (e: any) {
            // API获取失败，静默处理
          }
        }))
      }
      
      const allMessages = chatStore.messages
      
      const chatUsers = chats.map(chat => {
        let targetId = chat.to_id
        // 如果是群组，统一标准化ID格式
        if (chat.chat_type === 2) {
          targetId = standardizeGroupId(targetId)
        }
        
        // 如果是群组，先验证群组是否存在
        if (chat.chat_type === 2) {
          // 如果群组不在有效列表中，尝试从缓存恢复（可能是刚创建的群组）
          if (!validGroupIds.has(targetId)) {
            // 检查缓存中是否有群组信息
            if (groupsCache.value.has(targetId)) {
              const cachedGroup = groupsCache.value.get(targetId)!
              if (cachedGroup.group_name && 
                  cachedGroup.group_name !== targetId && 
                  cachedGroup.group_name.trim() !== '' &&
                  !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(cachedGroup.group_name)) {
                // 缓存中有有效信息，添加到有效列表
                validGroupIds.add(targetId)
                groupInfoMap.set(targetId, {
                  id: targetId,
                  name: cachedGroup.group_name,
                  member_count: cachedGroup.member_count,
                })
              } else {
                return null
              }
            } else {
              // 缓存中也没有，且API获取也失败了，跳过这个聊天记录
              return null
            }
          }
          // 优先使用后端返回的名称和人数
          let groupName = chat.name
          let memberCount = chat.member_count
          
          // 判断是否为UUID格式（用于检查名称是否有效）
          const isUUID = (str: string): boolean => {
            const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
            return uuidRegex.test(str)
          }
          
          // 检查是否是用户名格式（短字符串，不包含特殊字符，不是UUID）
          const isLikelyUsername = (str: string): boolean => {
            if (!str || str.trim() === '') return false
            if (isUUID(str)) return false
            // 如果长度小于20且不包含特殊字符，可能是用户名
            if (str.length < 20 && /^[a-zA-Z0-9_-]+$/.test(str)) {
              // 但如果是纯数字（可能是ID），不算用户名
              if (/^\d+$/.test(str)) return false
              return true
            }
            return false
          }
          
          // 如果后端没有提供名称（null、undefined、空字符串）或名称等于ID或是UUID格式，或者是用户名格式，尝试从多个来源获取
          // 或者如果后端返回的名称就是ID本身，也需要从本地缓存获取
          if (!groupName || groupName.trim() === '' || groupName === targetId || isUUID(groupName) || isLikelyUsername(groupName)) {
            // 1. 优先从 groupInfoMap 获取（已经通过 Promise.all 加载完成）
            const groupInfo = groupInfoMap.get(targetId)
            if (groupInfo && groupInfo.name && groupInfo.name !== targetId && groupInfo.name.trim() !== '' && !isUUID(groupInfo.name)) {
              groupName = groupInfo.name
              if (memberCount === undefined) {
                memberCount = groupInfo.member_count
              }
            } else {
              // 2. 如果 groupInfoMap 中没有，尝试从 allGroupsMap 获取（从本地缓存读取的群组列表）
              const groupFromList = allGroupsMap.get(targetId)
              if (groupFromList && groupFromList.group_name && groupFromList.group_name !== targetId && groupFromList.group_name.trim() !== '' && !isUUID(groupFromList.group_name)) {
                groupName = groupFromList.group_name
                if (memberCount === undefined) {
                  memberCount = groupFromList.member_count
                }
                // 同时更新 groupInfoMap，以便后续使用
                groupInfoMap.set(targetId, {
                  id: targetId,
                  name: groupFromList.group_name,
                  member_count: groupFromList.member_count
                })
              } else {
                // 3. 如果都没有，尝试从缓存获取
                if (groupsCache.value.has(targetId)) {
                  const cachedGroup = groupsCache.value.get(targetId)!
                  if (cachedGroup.group_name && cachedGroup.group_name !== targetId && cachedGroup.group_name.trim() !== '' && !isUUID(cachedGroup.group_name)) {
                    groupName = cachedGroup.group_name
                    if (memberCount === undefined) {
                      memberCount = cachedGroup.member_count
                    }
                    // 同时更新 groupInfoMap
                    groupInfoMap.set(targetId, {
                      id: targetId,
                      name: cachedGroup.group_name,
                      member_count: cachedGroup.member_count
                    })
                  } else {
                    // 如果缓存中的名称也是UUID或无效，尝试再次获取群组信息
                    console.warn('群组名称无效，尝试重新获取群组信息:', targetId)
                    // 异步获取群组信息（不阻塞当前流程）
                    import('../services/im-group').then(({ getGroup }) => {
                      return getGroup(targetId)
            }).then(groupInfo => {
              if (groupInfo && groupInfo.group_name && !isUUID(groupInfo.group_name)) {
                groupsCache.value.set(targetId, {
                  group_name: groupInfo.group_name,
                  member_count: groupInfo.member_count
                })
                // 更新最近聊天列表中的群组名称
                const chatUser = recentChatUsers.value.find(u => u.id === targetId)
                if (chatUser) {
                  chatUser.name = groupInfo.group_name
                }
              }
            }).catch(() => {})
                    // 暂时使用ID作为后备
                    groupName = targetId
                  }
                } else {
                  // 如果缓存中也没有，尝试异步获取群组信息
                  console.warn('群组信息不在缓存中，尝试获取群组信息:', targetId)
                  import('../services/im-group').then(({ getGroup }) => {
                    return getGroup(targetId)
            }).then(groupInfo => {
              if (groupInfo && groupInfo.group_name && !isUUID(groupInfo.group_name)) {
                groupsCache.value.set(targetId, {
                  group_name: groupInfo.group_name,
                  member_count: groupInfo.member_count
                })
                // 更新最近聊天列表中的群组名称
                const chatUser = recentChatUsers.value.find(u => u.id === targetId)
                if (chatUser) {
                  chatUser.name = groupInfo.group_name
                }
              }
            }).catch(() => {})
                  // 暂时使用ID作为后备
                  groupName = targetId
                }
              }
            }
            } else {
              // 如果后端返回的名称有效，但我们也应该检查一下 allGroupsMap 是否有更完整的信息（比如成员数）
            const groupFromList = allGroupsMap.get(targetId)
            if (groupFromList && groupFromList.member_count !== undefined && memberCount === undefined) {
              memberCount = groupFromList.member_count
            }
          }
          
          // 最终检查：如果群组名称看起来像用户名（短字符串，不是UUID），可能是错误的
          // 如果群组名称看起来像用户名，尝试再次获取或使用更明确的标识
          if (groupName && isLikelyUsername(groupName)) {
            console.warn('⚠️ 群组名称看起来像用户名，尝试重新获取:', targetId, '当前名称:', groupName)
            // 尝试异步获取群组信息
            import('../services/im-group').then(({ getGroup }) => {
              return getGroup(targetId)
            }).then(groupInfo => {
              if (groupInfo && groupInfo.group_name && !isUUID(groupInfo.group_name) && !isLikelyUsername(groupInfo.group_name)) {
                groupsCache.value.set(targetId, {
                  group_name: groupInfo.group_name,
                  member_count: groupInfo.member_count
                })
                // 更新最近聊天列表中的群组名称
                const chatUser = recentChatUsers.value.find(u => u.id === targetId && (u as any).chat_type === 2)
                if (chatUser) {
                  chatUser.name = groupInfo.group_name
                }
              }
            }).catch(() => {})
            // 暂时使用 "群组 {targetId}" 作为后备，避免显示用户名
            groupName = `群组 ${targetId.substring(0, 8)}${targetId.length > 8 ? '...' : ''}`
          }
          
          // 如果获取到了有效的名称，更新缓存
          if (groupName && groupName !== targetId && groupName.trim() !== '' && !isUUID(groupName) && !isLikelyUsername(groupName)) {
            groupsCache.value.set(targetId, {
              group_name: groupName,
              member_count: memberCount
            })
          }
          
          // 获取最后一条消息的时间（群组消息的 to_user_id 是群组ID）
          // 使用标准化的群组ID进行匹配
          const standardizedTargetId = standardizeGroupId(targetId)
          const lastMessage = allMessages
            .filter(msg => {
              // 确保只匹配群聊类型的消息
              if (msg.chat_type !== 2) {
                return false
              }
              const msgToId = String(msg.to_user_id || '').trim()
              const normalizedMsgToId = normalizeGroupId(msgToId)
              const normalizedTargetId = normalizeGroupId(standardizedTargetId)
              return msgToId === standardizedTargetId || normalizedMsgToId === normalizedTargetId
            })
            .sort((a, b) => {
              // 使用 timestamp_ms 如果存在，否则使用 timestamp
              const aTime = a.timestamp_ms || a.timestamp
              const bTime = b.timestamp_ms || b.timestamp
              return bTime - aTime
            })[0]
          
          return {
            id: standardizedTargetId, // 使用标准化的群组ID
            name: groupName,
            email: standardizedTargetId, // 群组没有email，使用ID
            open_id: standardizedTargetId,
            chat_id: chat.chat_id,
            chat_type: chat.chat_type,
            member_count: memberCount, // 添加群人数
            sequence: chat.sequence, // 传递未读消息序列号
            read_sequence: chat.read_sequence, // 传递已读消息序列号
            lastTime: lastMessage ? (lastMessage.timestamp_ms || lastMessage.timestamp) : (chat.update_time || 0),
          }
        } else {
          // 如果是用户，使用用户信息
          // 优先使用后端返回的名称
          let userName = chat.name
          
          // 判断是否为 openid 格式（纯数字或UUID）
          const isOpenIdFormat = (str: string): boolean => {
            if (!str) return false
            // 纯数字（可能是雪花ID或open_id）
            if (/^\d+$/.test(str)) return true
            // UUID格式
            if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(str)) return true
            return false
          }
          
          // 如果后端没有提供名称，或者提供的名称看起来像 openid，使用之前获取的用户信息
          if (!userName || userName === targetId || isOpenIdFormat(userName)) {
            const user = userInfoMap.get(targetId)
            if (user && user.name && user.name !== targetId && !isOpenIdFormat(user.name)) {
              userName = user.name
            } else if (!userName || userName === targetId || isOpenIdFormat(userName)) {
              // 如果 userInfoMap 中也没有有效的用户名，尝试使用 targetId 作为后备
              // 但优先使用 userInfoMap 中的 name（即使可能是 openid）
              userName = (user && user.name) || userName || targetId
            }
          }
          
          const user = userInfoMap.get(targetId) || {
            id: targetId,
            name: userName,
            email: targetId,
            open_id: targetId,
          }
          
          // 如果后端提供了有效的名称（不是 openid 格式），更新用户信息
          if (userName && userName !== targetId && !isOpenIdFormat(userName)) {
            user.name = userName
          } else if (userInfoMap.has(targetId)) {
            // 如果后端返回的是 openid，但 userInfoMap 中有有效的用户名，使用 userInfoMap 中的
            const mappedUser = userInfoMap.get(targetId)!
            if (mappedUser.name && mappedUser.name !== targetId && !isOpenIdFormat(mappedUser.name)) {
              user.name = mappedUser.name
            }
          }
          
          // 获取最后一条消息的时间
          const lastMessage = allMessages
            .filter(msg => {
              // 确保只匹配相同类型的消息
              if (msg.chat_type !== chat.chat_type) {
                return false
              }
              if (msg.isReceived) {
                return String(msg.from_user_id || '') === (user.open_id || user.id)
              } else {
                return String(msg.to_user_id || '') === (user.open_id || user.id)
              }
            })
            .sort((a, b) => {
              // 使用 timestamp_ms 如果存在，否则使用 timestamp
              const aTime = a.timestamp_ms || a.timestamp
              const bTime = b.timestamp_ms || b.timestamp
              return bTime - aTime
            })[0]
          
          return {
            ...user,
            chat_id: chat.chat_id,
            chat_type: chat.chat_type,
            sequence: chat.sequence, // 传递未读消息序列号
            read_sequence: chat.read_sequence, // 传递已读消息序列号
            lastTime: lastMessage ? (lastMessage.timestamp_ms || lastMessage.timestamp) : (chat.update_time || 0),
          }
        }
      }).filter((chat): chat is User => chat !== null) // 过滤掉 null（不存在的群组）
      // 不在这里排序，保持原始顺序（点击时不改变顺序）
      // 排序将在发送消息后或消息更新时进行
      
      // 去重：同一个用户/群组只保留最新的聊天记录
      // 对于单聊，优先使用 open_id 作为唯一标识（如果存在），否则使用 id
      // 对于群聊，使用 id（群组ID）作为唯一标识
      // 注意：同一个用户可以有单聊和群聊，它们是不同的聊天，应该分别显示
      const uniqueChatMap = new Map<string, User>()
      chatUsers.forEach(chat => {
        const chatType = chat.chat_type || 1
        
        // 对于群聊，使用群组ID作为唯一标识
        // 对于单聊，使用 open_id 或 id 作为唯一标识
        // 使用 chat_type 作为键的一部分，确保单聊和群聊分开处理
        let uniqueIdentifier: string
        if (chatType === 2) {
          // 群聊：使用群组ID作为唯一标识
          uniqueIdentifier = chat?.id || ''
        } else {
          // 单聊：优先使用 open_id，否则使用 id
          uniqueIdentifier = (chat?.open_id && chat.open_id !== chat?.id) ? chat.open_id : chat?.id || ''
        }
        
        const uniqueKey = `${uniqueIdentifier}_${chatType}` // 使用唯一标识符和 chat_type 作为唯一键
        
        // 检查是否已存在相同的记录（必须是相同的 chat_type）
        let existingKey: string | null = null
        for (const [key, existing] of uniqueChatMap.entries()) {
          const existingChatType = (existing as any).chat_type || 1
          const currentChatType = chat?.chat_type || 1
          
          // 必须 chat_type 相同才能匹配
          if (existingChatType !== currentChatType) continue
          
          // 对于群聊，必须ID完全匹配（考虑 group_ 前缀）
          if (currentChatType === 2) {
            // 注意：normalizeGroupId 已经在函数开头定义
            const existingIdNormalized = normalizeGroupId(existing.id || '')
            const chatIdNormalized = normalizeGroupId(chat?.id || '')
            
            // 完全匹配或标准化后匹配
            if (existing.id === chat?.id || existingIdNormalized === chatIdNormalized) {
              existingKey = key
              break
            }
          } else {
            // 对于单聊，检查 id 或 open_id 匹配
            const idMatch = existing.id === chat?.id || 
                           existing.open_id === chat?.open_id ||
                           (existing.open_id && existing.open_id === chat?.id || '') ||
                           (chat?.open_id && chat?.open_id === existing.id)
            
            // 条件2：name 相同（用于处理用户名和ID混用的情况）
            const nameMatch = existing.name === chat?.name && 
                             existing.name && 
                             chat.name &&
                             existing.name !== existing.id && // name 不能等于 id（避免误判）
                             chat.name !== chat.id &&
                             existing.name !== existing.open_id && // name 不能等于 open_id（避免误判）
                             chat.name !== chat.open_id
            
            if (idMatch || nameMatch) {
              existingKey = key
              break
            }
          }
        }
        
        if (existingKey) {
          // 如果已存在，比较时间戳，保留最新的
          const existing = uniqueChatMap.get(existingKey)!
          if ((chat?.lastTime || 0) > ((existing as any).lastTime || 0)) {
            uniqueChatMap.set(existingKey, chat!)
          }
        } else {
          // 如果不存在，添加新记录
          uniqueChatMap.set(uniqueKey, chat!)
        }
      })
      const uniqueChatUsers = Array.from(uniqueChatMap.values())
      
      // 检查是否有群组没有被包含在聊天列表中
      // 如果有群组在 allGroupsMap 中，但没有对应的聊天记录，需要创建聊天项
      const existingGroupIds = new Set<string>()
      uniqueChatUsers.forEach(user => {
        if ((user as any).chat_type === 2) {
          existingGroupIds.add(user.id)
        }
      })
      
      // 从 allGroupsMap 中找出没有聊天记录的群组，创建聊天项
      // 先标准化所有已存在的群组ID，用于比较
      // 注意：normalizeGroupId 和 standardizeGroupId 已经在函数开头定义
      const normalizedExistingGroupIds = new Set<string>()
      existingGroupIds.forEach(id => {
        normalizedExistingGroupIds.add(normalizeGroupId(id))
      })
      
      for (const [groupId, groupInfo] of allGroupsMap.entries()) {
        const normalizedGroupId = normalizeGroupId(groupId)
        if (!existingGroupIds.has(groupId) && !normalizedExistingGroupIds.has(normalizedGroupId)) {
          // 检查是否有该群组的消息（从本地消息中查找）
          const hasGroupMessages = allMessages.some(msg => {
            const msgToId = String(msg.to_user_id || '').trim()
            const normalizedMsgToId = normalizeGroupId(msgToId)
            return msgToId === groupId || normalizedMsgToId === normalizedGroupId
          })
          
          // 如果有消息或者群组在缓存中，创建聊天项
          // 检查缓存时也要考虑标准化ID
          const hasInCache = groupsCache.value.has(groupId) || 
                             Array.from(groupsCache.value.keys()).some(cachedId => 
                               normalizeGroupId(cachedId) === normalizedGroupId
                             )
          
          if (hasGroupMessages || hasInCache) {
            const lastMessage = allMessages
              .filter(msg => {
                // 确保只匹配群聊类型的消息
                if (msg.chat_type !== 2) {
                  return false
                }
                const msgToId = String(msg.to_user_id || '').trim()
                const normalizedMsgToId = normalizeGroupId(msgToId)
                return msgToId === groupId || normalizedMsgToId === normalizedGroupId
              })
              .sort((a, b) => {
                // 使用 timestamp_ms 如果存在，否则使用 timestamp
                const aTime = a.timestamp_ms || a.timestamp
                const bTime = b.timestamp_ms || b.timestamp
                return bTime - aTime
              })[0]
            
            // 使用标准化的群组ID（统一使用带 group_ 前缀的格式）
            const standardizedGroupId = groupId.startsWith('group_') ? groupId : `group_${groupId}`
            
            const groupUser: User = {
              id: standardizedGroupId,
              name: groupInfo.group_name,
              email: standardizedGroupId,
              open_id: standardizedGroupId,
              chat_type: 2,
              member_count: groupInfo.member_count,
              lastTime: lastMessage ? (lastMessage.timestamp_ms || lastMessage.timestamp) : 0,
            }
            
            const uniqueKey = `${standardizedGroupId}_2`
            uniqueChatMap.set(uniqueKey, groupUser)
          }
        }
      }
      
      // 重新获取去重后的聊天列表（包含新创建的群组聊天项）
      const finalChatUsers = Array.from(uniqueChatMap.values())
      
      // 如果后端有聊天记录，使用后端的记录（已去重）
      if (finalChatUsers.length > 0) {
        // 如果是第一次加载（列表为空），使用后端返回的顺序
        if (recentChatUsers.value.length === 0) {
          recentChatUsers.value = finalChatUsers
        } else {
          // 保持原有顺序，只更新数据而不改变顺序
          // 创建一个映射，用于快速查找新数据
          const newDataMap = new Map<string, User>()
          finalChatUsers.forEach(user => {
            const key = `${user.id}_${(user as any).chat_type || 1}`
            newDataMap.set(key, user)
          })
          
          // 保持原有顺序，只更新数据
          const existingOrder = recentChatUsers.value.map(existing => {
            const key = `${existing.id}_${(existing as any).chat_type || 1}`
            const newData = newDataMap.get(key)
            if (newData) {
              // 更新数据但保持位置
              return { ...newData }
            }
            return existing
          })
          
          // 添加新出现的聊天（不在原有列表中的）
          const existingKeys = new Set(existingOrder.map(u => `${u.id}_${(u as any).chat_type || 1}`))
          finalChatUsers.forEach(newUser => {
            const key = `${newUser.id}_${(newUser as any).chat_type || 1}`
            if (!existingKeys.has(key)) {
              existingOrder.push(newUser)
            }
          })
          
          recentChatUsers.value = existingOrder
        }
        return
      }
      
      // 如果后端没有聊天记录，但从本地消息中提取
      await extractRecentChatsFromMessages()
    } catch (error: any) {
      // 如果是网络错误或 CORS 错误，静默处理，使用本地消息
      if (error.message && (
        error.message.includes('Failed to fetch') ||
        error.message.includes('Could not connect') ||
        error.message.includes('CORS') ||
        error.message.includes('NetworkError')
      )) {
        if (import.meta.env.DEV) {
          console.debug('获取聊天列表失败（网络错误），使用本地消息:', error.message)
        }
        // 使用本地消息提取聊天列表
        await extractRecentChatsFromMessages()
        return
      }
      console.error('获取聊天列表失败:', error)
      // 即使出错也尝试使用本地消息
      try {
        await extractRecentChatsFromMessages()
      } catch (e) {
        console.error('从本地消息提取聊天列表也失败:', e)
      }
    }
  }
  
  // 从本地消息中提取最近聊天列表
  const extractRecentChatsFromMessages = async () => {
    const allMessages = chatStore.messages
    
    if (allMessages.length === 0) {
      recentChatUsers.value = []
      return
    }
    
    // 先加载群组列表（从本地缓存）
    const { loadGroupsFromCache } = await import('../services/im-group')
    const cachedGroups = await loadGroupsFromCache()
    const allGroupsMap = new Map<string, { group_name: string; member_count?: number }>()
    if (cachedGroups && cachedGroups.length > 0) {
      cachedGroups.forEach(group => {
        allGroupsMap.set(group.group_id, {
          group_name: group.group_name,
          member_count: group.member_count
        })
      })
    }
    
    const chatMap = new Map<string, { user: User; lastTime: number; chat_type?: number; member_count?: number }>()
    
    allMessages.forEach(msg => {
      const userId = msg.isReceived ? msg.from_user_id : msg.to_user_id
      // 使用 timestamp_ms 如果存在，否则使用 timestamp
      const lastTime = msg.timestamp_ms || msg.timestamp
      const chatType = msg.chat_type
      
      if (!userId) return
      
      // 判断是否为群组聊天：以 chat_type === 2 为主判断，人数只能作为辅助
      // 重要：不能以人数超过2人为准，还是要以 chat_type 是 2 为主
      // 原因：有可能开始拉群人数超过2个人，后面群主把人员移除群聊，这个群就剩下他一个人
      // 如果以人数判断，就会有bug
      const groupFromMap = allGroupsMap.get(userId)
      const memberCount = groupFromMap?.member_count
      // 优先根据 chat_type 判断：chat_type === 2 为群聊
      // 如果 chat_type 不存在，则检查是否在群组列表中（作为辅助判断）
      const isGroupChat = chatType === 2 || 
                         (chatType === undefined && allGroupsMap.has(userId))
      
      if (isGroupChat) {
        // 群组聊天（chat_type === 2）
        const groupName = groupFromMap?.group_name || userId
        
        const groupUser: User = {
          id: userId,
          name: groupName,
          email: userId,
          open_id: userId,
        }
        
        const uniqueId = userId
        
        if (!chatMap.has(uniqueId) || chatMap.get(uniqueId)!.lastTime < lastTime) {
          chatMap.set(uniqueId, { 
            user: groupUser, 
            lastTime,
            chat_type: 2, // 群聊
            member_count: memberCount
          })
        }
      } else {
        // 单聊
        // 在现有列表中查找用户（优先匹配 open_id，然后才是 id 和 snowflake_id）
        let user = userList.value.find(u => 
          u.open_id === userId || 
          u.id === userId || 
          (u.snowflake_id && String(u.snowflake_id) === userId)
        )
        
        if (!user) {
          user = friendList.value.find(u => 
            u.open_id === userId || 
            u.id === userId || 
            (u.snowflake_id && String(u.snowflake_id) === userId)
          )
        }
        
        if (!user) {
          // 如果找不到，尝试通过其他标识符查找（可能是 open_id 和 id 互换的情况）
          // 先尝试在 userList 中查找是否有 open_id 等于当前 userId 的记录
          user = userList.value.find(u => u.open_id === userId || u.id === userId)
          if (!user) {
            user = friendList.value.find(u => u.open_id === userId || u.id === userId)
          }
        }
        
        if (!user) {
          // 如果通过 id/open_id 找不到，尝试通过 name 查找（可能是用户名的情况）
          // 先检查 userId 是否是用户名格式（不是纯数字）
          const isUsernameFormat = isNaN(Number(userId)) && userId !== ''
          if (isUsernameFormat) {
            // 尝试通过 name 查找
            user = userList.value.find(u => u.name === userId)
            if (!user) {
              user = friendList.value.find(u => u.name === userId)
            }
          }
          
          // 如果还是找不到，创建临时用户对象
          if (!user) {
            user = {
              id: userId,
              name: userId,
              email: userId,
              open_id: userId,
            }
          }
        }
        
        // 统一使用 open_id 作为唯一标识符（如果存在且有效），否则使用 id
        // 优先使用数字格式的 open_id（通常是真实的用户ID）
        const uniqueId = (user.open_id && user.open_id !== user.id && !isNaN(Number(user.open_id))) 
          ? user.open_id 
          : ((user.open_id && user.open_id !== user.id) ? user.open_id : (user.id || userId))
        
        // 检查 chatMap 中是否已存在相同的用户
        // 匹配条件：1) id 或 open_id 匹配，2) 或者 name 相同且 chat_type 相同（用于处理用户名和ID混用的情况）
        let existingKey: string | null = null
        for (const [key, existing] of chatMap.entries()) {
          const existingUser = existing.user
          const existingChatType = existing.chat_type || 1
          const currentChatType = 1
          
          // 如果 chat_type 不同，跳过
          if (existingChatType !== currentChatType) continue
          
          // 条件1：id 或 open_id 匹配
          const idMatch = existingUser.open_id === user.open_id || 
                         existingUser.id === user.id ||
                         (existingUser.open_id && existingUser.open_id === user.id) ||
                         (user.open_id && user.open_id === existingUser.id)
          
          // 条件2：name 相同（用于处理用户名和ID混用的情况）
          const nameMatch = existingUser.name === user.name && 
                           existingUser.name && 
                           user.name &&
                           existingUser.name !== existingUser.id && // name 不能等于 id（避免误判）
                           user.name !== user.id
          
          if (idMatch || nameMatch) {
            existingKey = key
            break
          }
        }
        
        if (existingKey) {
          // 如果已存在，更新为最新的消息时间
          const existing = chatMap.get(existingKey)!
          if (lastTime > existing.lastTime) {
            chatMap.set(existingKey, { user, lastTime, chat_type: 1 })
          }
        } else {
          // 如果不存在，添加新记录
          chatMap.set(uniqueId, { user, lastTime, chat_type: 1 })
        }
      }
    })
    
    const extractedChats = Array.from(chatMap.values())
      // 不排序，保持消息提取的原始顺序（点击时不改变顺序）
      .map(item => ({
        ...item.user,
        chat_type: item.chat_type,
        member_count: item.member_count,
        lastTime: item.lastTime
      }))
    
    // 去重：同一个用户/群组只保留最新的聊天记录
    // 对于单聊，优先使用 open_id 作为唯一标识（如果存在），否则使用 id
    // 对于群聊，使用 id（群组ID）作为唯一标识
    const uniqueChatMap = new Map<string, User>()
    extractedChats.forEach(chat => {
      // 确定唯一标识符：优先使用 open_id（如果存在且有效），否则使用 id
      const uniqueIdentifier = (chat.open_id && chat.open_id !== chat.id) ? chat.open_id : chat.id
      const uniqueKey = `${uniqueIdentifier}_${chat.chat_type || 1}` // 使用唯一标识符和 chat_type 作为唯一键
      
      // 检查是否已存在相同的记录
      // 匹配条件：1) id 或 open_id 匹配，2) 或者 name 相同且 chat_type 相同（用于处理用户名和ID混用的情况）
      let existingKey: string | null = null
      for (const [key, existing] of uniqueChatMap.entries()) {
        const existingChatType = (existing as any).chat_type || 1
        const currentChatType = chat.chat_type || 1
        
        // 如果 chat_type 不同，跳过
        if (existingChatType !== currentChatType) continue
        
        // 条件1：id 或 open_id 匹配
        const idMatch = existing.id === chat.id || 
                       existing.open_id === chat.open_id ||
                       (existing.open_id && existing.open_id === chat.id) ||
                       (chat.open_id && chat.open_id === existing.id)
        
        // 条件2：name 相同（用于处理用户名和ID混用的情况）
        const nameMatch = existing.name === chat.name && 
                         existing.name && 
                         chat.name &&
                         existing.name !== existing.id && // name 不能等于 id（避免误判）
                         chat.name !== chat.id &&
                         existing.name !== existing.open_id && // name 不能等于 open_id（避免误判）
                         chat.name !== chat.open_id
        
        if (idMatch || nameMatch) {
          existingKey = key
          break
        }
      }
      
      if (existingKey) {
        // 如果已存在，比较时间戳，保留最新的
        const existing = uniqueChatMap.get(existingKey)!
        if ((chat.lastTime || 0) > ((existing as any).lastTime || 0)) {
          uniqueChatMap.set(existingKey, chat)
        }
      } else {
        // 如果不存在，添加新记录
        uniqueChatMap.set(uniqueKey, chat)
      }
    })
    recentChatUsers.value = Array.from(uniqueChatMap.values())
  }

  // 加载用户列表
  const loadUserList = async () => {
    await loadFriends()
    // 异步更新最近聊天列表（因为可能需要转换雪花ID）
    updateRecentChatUsers().catch(err => {
      console.error('更新最近聊天列表失败:', err)
    })
  }

  // 选择用户（根据 chat_type 自动判断是否为群组：chat_type === 2 为群组）
  const selectUser = async (userId: string) => {
    // 清空输入框和已选文件，避免切换聊天对象时保留之前的内容
    inputContent.value = ''
    selectedFiles.value = []
    
    // 从最近聊天列表中查找，获取成员数信息
    const normalizedUserId = normalizeGroupId(userId)
    const chatInfo = recentChatUsers.value.find(u => {
      if (!u.id) return false
      const normalizedUId = normalizeGroupId(u.id)
      return u.id === userId || normalizedUId === normalizedUserId
    })
    
    const memberCount = (chatInfo as any)?.member_count
    const chatType = (chatInfo as any)?.chat_type
    
    // 判断是否为群组：
    // 1. 成员数 >= 3
    // 2. 或者 chat_type === 2
    // 3. 或者 ID 以 group_ 开头
    const isGroup = (memberCount !== undefined && memberCount >= 3) ||
                    chatType === 2 ||
                    userId.startsWith('group_')
    
    await chatStore.setTarget(userId, isGroup ? 'Group' : 'User')
    // 等待消息加载和渲染完成后再滚动到底部
    await nextTick()
    // 使用多次延迟确保消息完全加载和渲染
    setTimeout(() => {
      scrollToBottom()
      // 再次延迟，确保DOM完全更新
      setTimeout(() => {
        scrollToBottom()
      }, 200)
    }, 150)
    // 已读序列号更新由 MessageList 组件自动处理（当滚动到底部时）
  }

  // 选择群组（可选传入群组名称，避免等待API调用）
  const selectGroup = async (groupId: string, groupName?: string) => {
    // 清空输入框和已选文件，避免切换聊天对象时保留之前的内容
    inputContent.value = ''
    selectedFiles.value = []
    
    await chatStore.setTarget(groupId, 'Group')
    
    // 如果提供了群组名称，更新缓存（targetName computed 会直接从 recentChatUsers 获取）
    if (groupName && isValidGroupName(groupName, groupId)) {
      const existingCache = groupsCache.value.get(groupId)
      groupsCache.value.set(groupId, { 
        group_name: groupName,
        member_count: existingCache?.member_count 
      })
    }
    
    // 异步加载群组信息来更新缓存（不阻塞，因为 targetName computed 已经处理了显示）
    loadTargetGroupInfo(groupId).catch(err => {
      console.warn('后台更新群组信息失败:', err)
    })
    
    // 立即加载群成员信息到缓存（用于显示群昵称）- 等待完成以确保消息渲染时能正确显示
    try {
      const { getGroupMembers } = await import('../services/im-group')
      const { getUser } = await import('../services/message')
      const normalizedGroupId = groupId.replace(/^group_/i, '').trim().toLowerCase()
      
      const members = await getGroupMembers(normalizedGroupId, true) // 使用缓存
      
      // 获取每个成员的用户信息
      const membersWithUserInfo = await Promise.all(
        members.map(async (member) => {
          try {
            const userInfo = await getUser(member.member_id)
            return {
              ...member,
              displayName: userInfo?.name || member.alias || member.member_id,
            }
          } catch (e: any) {
            return {
              ...member,
              displayName: member.alias || member.member_id,
            }
          }
        })
      )
      
      // 更新群成员缓存
      updateGroupMembersCache(normalizedGroupId, membersWithUserInfo.map(m => ({
        member_id: m.member_id,
        alias: m.alias,
        displayName: m.displayName,
        name: m.displayName || m.alias || m.member_id,
      })))
      
      // 触发响应式更新，确保 Vue 能检测到缓存变化
      triggerRef(groupMembersCache)
    } catch (error) {
      console.warn('加载群成员信息失败（不影响使用）:', error)
    }
    
    // 等待消息加载和渲染完成后再滚动到底部
    await nextTick()
    // 使用多次延迟确保消息完全加载和渲染
    setTimeout(() => {
      scrollToBottom()
      // 再次延迟，确保DOM完全更新
      setTimeout(() => {
        scrollToBottom()
      }, 200)
    }, 150)
    // 已读序列号更新由 MessageList 组件自动处理（当滚动到底部时）
  }

  // 发送消息
  const handleSend = async () => {
    if (!inputContent.value.trim() && selectedFiles.value.length === 0) {
      $q.notify({
        type: 'warning',
        message: '消息内容不能为空',
      })
      return
    }

    // 检查 WebSocket 连接状态，如果断开则尝试重连（用于接收消息）
    if (!isConnected.value && chatStore.subscriptionId) {
      try {
        await chatStore.connect(chatStore.subscriptionId)
        // 等待一小段时间让连接建立
        await new Promise(resolve => setTimeout(resolve, 500))
      } catch (error) {
        console.warn('WebSocket 重连失败，但继续发送消息（通过 HTTP API）:', error)
        // 即使重连失败，也继续发送消息（因为消息通过 HTTP API 发送）
      }
    } else if (!isConnected.value && !chatStore.subscriptionId) {
      // 如果没有订阅ID，尝试从存储中获取
      const { isTauri, storageGet } = await import('../services/secure-storage')
      const subscriptionId = isTauri()
        ? (await storageGet('subscriptionId')) || ''
        : localStorage.getItem('subscriptionId') || ''
      
      if (subscriptionId) {
        try {
          await chatStore.connect(subscriptionId)
          await new Promise(resolve => setTimeout(resolve, 500))
        } catch (error) {
          console.warn('WebSocket 连接失败，但继续发送消息（通过 HTTP API）:', error)
        }
      }
    }

    sending.value = true
    try {
      // 处理文件：每个文件单独发送一条消息
      if (selectedFiles.value.length > 0) {
        uploading.value = true
        try {
          // 分离图片文件和其他文件
          const imageFiles = selectedFiles.value.filter(file => isImageFile(file))
          const otherFiles = selectedFiles.value.filter(file => !isImageFile(file))

          // 处理图片文件
          for (const file of imageFiles) {
            try {
              // 检查文件大小，如果太大则压缩
              let fileToProcess = file
              const fileSizeKB = file.size / 1024
              
              // 压缩策略：根据文件大小选择不同的压缩参数
              if (fileSizeKB > 300) {
                try {
                  // 根据文件大小选择压缩参数
                  let maxWidth = 1920
                  let maxHeight = 1920
                  let quality = 0.8
                  
                  if (fileSizeKB > 2000) {
                    // 超大图片：更激进的压缩
                    maxWidth = 1280
                    maxHeight = 1280
                    quality = 0.6
                  } else if (fileSizeKB > 1000) {
                    // 大图片：中等压缩
                    maxWidth = 1600
                    maxHeight = 1600
                    quality = 0.7
                  } else if (fileSizeKB > 500) {
                    // 中等图片：轻度压缩
                    maxWidth = 1920
                    maxHeight = 1920
                    quality = 0.75
                  }
                  
                  fileToProcess = await compressImage(file, maxWidth, maxHeight, quality)
                  const compressedSizeKB = fileToProcess.size / 1024
                  
                  // 如果压缩后还是太大，再次压缩
                  if (compressedSizeKB > 800) {
                    fileToProcess = await compressImage(fileToProcess, 1280, 1280, 0.65)
                  }
                } catch (compressError) {
                  console.warn('图片压缩失败，使用原图:', compressError)
                }
              }
              
              // 上传图片到服务器
              const { uploadFile } = await import('../services/message')
              const uploadResult = await uploadFile(fileToProcess)
              
              // 如果是相对路径，拼接完整的 http/https URL
              let imageUrl = uploadResult.url
              if (imageUrl.startsWith('/api/upload/')) {
                const { API_BASE_URL } = await import('@/config/env')
                imageUrl = `${API_BASE_URL}${imageUrl}`
              }
              
              // 发送图片消息（每张图片单独发送一条消息）
              await chatStore.sendMessage(
                imageUrl, // 完整的图片 URL 作为消息内容
                MessageContentType.IMAGE,
                uploadResult.file_name,
                uploadResult.file_type
              )
            } catch (error: any) {
              console.error('发送图片失败:', error)
              $q.notify({
                type: 'negative',
                message: `图片 ${file.name} 发送失败: ${error.message || '上传失败'}`,
              })
            }
          }

          // 处理其他文件（非图片）
          for (const file of otherFiles) {
            try {
              // 上传文件到服务器
              const { uploadFile } = await import('../services/message')
              const uploadResult = await uploadFile(file)
              
              // 如果是相对路径，拼接完整的 http/https URL
              let fileUrl = uploadResult.url
              if (fileUrl.startsWith('/api/upload/')) {
                const { API_BASE_URL } = await import('@/config/env')
                fileUrl = `${API_BASE_URL}${fileUrl}`
              }
              
              // 发送文件消息（每个文件单独发送一条消息）
              // 文件消息使用完整文件 URL 作为消息内容（和图片消息一样）
              // 注意：uploadResult.file_name 是服务器生成的 UUID 文件名
              // 我们需要在 extra 中保存原始文件名
              await chatStore.sendMessage(
                fileUrl, // 完整的文件 URL 作为消息内容（和图片消息一样）
                MessageContentType.FILE,
                file.name, // 使用原始文件名显示
                uploadResult.file_type,
                fileUrl, // 传递文件 URL（用于 file_url 字段）
                uploadResult.file_name // 传递服务器文件名（用于下载）
              )
            } catch (error: any) {
              console.error('发送文件失败:', error)
              $q.notify({
                type: 'negative',
                message: `文件 ${file.name} 发送失败: ${error.message || '上传失败'}`,
              })
            }
          }
          
          // 如果有文字，最后发送一条文字消息
          const hasText = inputContent.value && inputContent.value.trim()
          if (hasText) {
            await chatStore.sendMessage(
              inputContent.value.trim(), // 文字内容
              MessageContentType.TEXT,
              undefined,
              undefined
            )
          }
          
          inputContent.value = ''
          selectedFiles.value = []
          showEmojiPicker.value = false
          // 发送消息后，更新聊天列表排序（时间越早的排最上面）
          await updateRecentChatUsers()
          // 重新聚焦输入框
          nextTick(() => {
            scrollToBottom()
            if (inputContentRef.value) {
              const input = inputContentRef.value.$el?.querySelector('textarea') || inputContentRef.value.$el?.querySelector('input')
              if (input) {
                input.focus()
              } else if (inputContentRef.value.focus) {
                inputContentRef.value.focus()
              }
            }
          })
          return // 提前返回，避免重复调用 sendMessage
        } catch (error: any) {
          $q.notify({
            type: 'negative',
            message: error.message || '文件处理失败',
          })
          return
        } finally {
          uploading.value = false
        }
      }

      // 如果没有图片，正常发送文字消息
      if (selectedFiles.value.length === 0) {
        await chatStore.sendMessage(
          inputContent.value.trim(),
          MessageContentType.TEXT,
          undefined,
          undefined
        )
        inputContent.value = ''
        showEmojiPicker.value = false
        // 发送消息后，更新聊天列表排序（时间越早的排最上面）
        await updateRecentChatUsers()
        // 重新聚焦输入框
        nextTick(() => {
          scrollToBottom()
          if (inputContentRef.value) {
            // Quasar 的 q-input 组件需要通过 $el 或直接调用 focus 方法
            const input = inputContentRef.value.$el?.querySelector('textarea') || inputContentRef.value.$el?.querySelector('input')
            if (input) {
              input.focus()
            } else if (inputContentRef.value.focus) {
              inputContentRef.value.focus()
            }
          }
        })
      }
    } catch (error: any) {
      $q.notify({
        type: 'negative',
        message: error.message || '发送消息失败',
      })
    } finally {
      sending.value = false
    }
  }

  // 处理表情选择
  const handleEmojiSelect = (emoji: string) => {
    inputContent.value = (inputContent.value || '') + emoji
    showEmojiPicker.value = false
    // 重新聚焦输入框，使用 setTimeout 确保表情选择器完全关闭后再聚焦
    setTimeout(() => {
      if (inputContentRef.value) {
        // Quasar 的 q-input 组件需要通过 $el 或直接调用 focus 方法
        const input = inputContentRef.value.$el?.querySelector('textarea') || inputContentRef.value.$el?.querySelector('input')
        if (input) {
          (input as HTMLElement).focus()
          // 确保输入框获得焦点后，光标在文本末尾
          if (input instanceof HTMLTextAreaElement) {
            input.setSelectionRange(input.value.length, input.value.length)
          }
        } else if (inputContentRef.value.focus) {
          inputContentRef.value.focus()
        }
      }
    }, 100) // 延迟 100ms 确保表情选择器完全关闭
  }
  
  // 确保输入框获得焦点的辅助函数
  const focusInput = () => {
    nextTick(() => {
      if (inputContentRef.value) {
        const input = inputContentRef.value.$el?.querySelector('textarea') || inputContentRef.value.$el?.querySelector('input')
        if (input) {
          (input as HTMLElement).focus()
        } else if (inputContentRef.value.focus) {
          inputContentRef.value.focus()
        }
      }
    })
  }

  // 处理 Enter 键
  const handleEnterKey = (event?: KeyboardEvent) => {
    if (!inputContent.value.trim() && selectedFiles.value.length === 0) {
      return
    }
    // 阻止默认行为（已经在模板中处理，但这里再确保一下）
    if (event) {
      event.preventDefault()
      event.stopPropagation()
    }
    // 确保输入框有焦点，然后发送消息
    focusInput()
    handleSend()
  }

  // 处理 Shift+Enter
  const handleShiftEnter = () => {
    inputContent.value += '\n'
  }

  // 处理文件选择（支持多张图片）
  const handleFileSelect = (file: File) => {
    if (file) {
      // 如果是图片，添加到数组中；如果不是图片，替换为单个文件（兼容旧逻辑）
      if (isImageFile(file)) {
        selectedFiles.value.push(file)
      } else {
        // 非图片文件，暂时只支持单个文件
        selectedFiles.value = [file]
      }
    }
  }

  // 删除指定索引的文件，如果不传索引则删除最后一张
  const handleRemoveFile = (index?: number) => {
    if (selectedFiles.value.length > 0) {
      if (typeof index === 'number' && index >= 0 && index < selectedFiles.value.length) {
        // 删除指定索引的文件
        selectedFiles.value.splice(index, 1)
      } else {
        // 删除最后一张
        selectedFiles.value.pop()
      }
    }
  }

  // 添加好友
  const handleAddFriend = async (username?: string) => {
    const userInput = username || newFriendForm.value.username
    if (!userInput || !userInput.trim()) {
      $q.notify({
        type: 'warning',
        message: '请输入用户ID或用户名',
      })
      return
    }

    const usernameValue = userInput.trim()
    
    searchingFriend.value = true
    try {
      // 先尝试获取用户信息（可能是用户名或ID）
      let user
      try {
        user = await getUser(usernameValue)
      } catch (e) {
        // 如果获取用户失败，直接使用输入的值作为用户ID
        user = { id: usernameValue, name: usernameValue, email: usernameValue }
      }
      
      // 使用新的 IM 好友服务
      await addImFriend({
        to_id: user.id,
        add_source: 'search',
      })
      await loadFriends()
      
      showUserDialog.value = false
      newFriendForm.value = { username: '' }
      $q.notify({
        type: 'positive',
        message: `已添加好友: ${user.name}`,
      })
    } catch (error: any) {
      $q.notify({
        type: 'negative',
        message: error.message || '添加好友失败，请检查用户ID或用户名是否正确',
      })
    } finally {
      searchingFriend.value = false
    }
  }

  // 登出
  const handleLogout = async () => {
    try {
      await logout()
      await chatStore.disconnect(true)
    } catch (error) {
      console.error('退出登录失败:', error)
    }
  }

  // 监听消息变化，自动滚动到底部
  watch(currentMessages, () => {
    nextTick(() => {
      scrollToBottom()
    })
    // 异步更新最近聊天列表（因为可能需要转换雪花ID）
    updateRecentChatUsers().catch(err => {
      console.error('更新最近聊天列表失败:', err)
    })
  }, { deep: true })

  // 监听 currentTargetId 变化
  watch(currentTargetId, () => {
    nextTick(() => {
      scrollToBottom()
    })
  })

  // 监听左侧菜单切换，清空搜索框（可选，根据需求决定）
  watch(leftMenuActive, () => {
    // 切换菜单时，可以选择清空搜索框，或者保留搜索状态
    // 这里选择保留搜索状态，让用户体验更流畅
    // searchQuery.value = ''
  })

  // 监听所有消息变化，更新最近聊天列表
  watch(() => chatStore.messages, () => {
    // 异步更新最近聊天列表（因为可能需要转换雪花ID）
    updateRecentChatUsers().catch(err => {
      console.error('更新最近聊天列表失败:', err)
    })
  }, { deep: true })

  // 加载当前用户信息
  const loadCurrentUser = () => {
    const user = getCurrentUser()
    if (user) {
      currentUser.value = {
        name: user.name,
      }
    }
  }

  // 全局键盘事件处理（用于处理输入框失去焦点后的回车键）
  const handleGlobalKeyDown = (event: KeyboardEvent) => {
    // 如果按下回车键，且不在输入框中输入
    if (event.key === 'Enter' && !event.shiftKey) {
      // 检查当前焦点是否在输入框内或其他可输入元素内
      const activeElement = document.activeElement
      const isInInput = activeElement?.tagName === 'TEXTAREA' || 
                       activeElement?.tagName === 'INPUT' ||
                       activeElement?.isContentEditable ||
                       activeElement?.tagName === 'BUTTON' ||
                       activeElement?.closest('button') ||
                       activeElement?.closest('input') ||
                       activeElement?.closest('textarea')
      
      // 如果焦点不在输入框内，且有消息内容，且不在发送中，则发送消息
      if (!isInInput && 
          !sending.value && 
          (inputContent.value.trim() || selectedFiles.value.length > 0)) {
        event.preventDefault()
        event.stopPropagation()
        // 先聚焦输入框，然后发送
        focusInput()
        setTimeout(() => {
          handleSend()
        }, 50)
      }
    }
  }

  // 初始化
  const init = async () => {
    // 加载当前用户信息
    loadCurrentUser()
    
    // 设置当前用户ID到 store（只使用 open_id 作为唯一标识）
    // 注意：在 Tauri 环境中，登录时会同时更新 localStorage 作为缓存
    // 所以这里先尝试从 localStorage 读取（同步，快速）
    // 如果 localStorage 中没有，再尝试从安全存储读取（异步）
    const openId = localStorage.getItem('openId');
    if (openId) {
      chatStore.currentUserId = openId;
    } else {
      // 如果 localStorage 中没有，尝试从安全存储读取（Tauri 环境）
      const { isTauri, storageGet } = await import('../services/secure-storage');
      if (isTauri()) {
        const storedOpenId = await storageGet('openId');
        if (storedOpenId) {
          chatStore.currentUserId = storedOpenId;
          // 同步到 localStorage 作为缓存
          localStorage.setItem('openId', storedOpenId);
        }
      }
    }
    
    chatStore.initWebSocket()
    // 先设置用户ID，再加载消息（异步加载）
    chatStore.loadMessagesFromStorage().then(() => {
      // 加载完消息后，更新最近聊天列表（异步更新，因为可能需要转换雪花ID）
      updateRecentChatUsers().catch(err => {
        console.error('更新最近聊天列表失败:', err)
      })
    })
    
    // 获取订阅 ID（兼容 Tauri 和浏览器环境）
    // 注意：这里使用 localStorage 作为缓存，在 Tauri 环境中会在登录时同步更新
    const savedSubscriptionId = localStorage.getItem('subscriptionId');
    if (savedSubscriptionId && !chatStore.isConnected) {
      chatStore.connect(savedSubscriptionId).catch((error) => {
        console.error('连接WebSocket失败:', error)
      })
    }
    
    loadUserList()
    
    // 添加全局键盘事件监听（用于处理输入框失去焦点后的回车键）
    window.addEventListener('keydown', handleGlobalKeyDown)
  }

  // 清理
  const cleanup = () => {
    // 移除全局键盘事件监听
    window.removeEventListener('keydown', handleGlobalKeyDown)
    chatStore.disconnect().catch((error) => {
      console.error('断开WebSocket失败:', error)
    })
  }

  // 通话相关函数已移至 chat-window-call.ts
  // 使用通话模块的函数
  const handleVoiceCall = callModule.handleVoiceCall
  const handleVideoCall = callModule.handleVideoCall
  const handleGroupVoiceCall = callModule.handleGroupVoiceCall
  const handleGroupVideoCall = callModule.handleGroupVideoCall
  const handleCallEnded = callModule.handleCallEnded
  const handleCallInviteAccept = callModule.handleCallInviteAccept
  const handleCallInviteReject = callModule.handleCallInviteReject
  const handleCallInviteTimeout = callModule.handleCallInviteTimeout

  // 处理用户信息更新
  const handleUserUpdated = () => {
    // 更新当前用户信息（不显示提示，因为 UserSettings 已经显示了）
    const user = getCurrentUser()
    if (user) {
      currentUser.value = {
        name: user.name,
      }
    }
    // 重新加载用户列表，以便显示更新的头像和签名
    loadUserList()
  }

  // 处理删除聊天
  const handleDeleteChat = async (chatId: string, userId: string) => {
    try {
      const { deleteChat } = await import('../services/im-chat')
      await deleteChat(chatId)
      
      // 如果删除的是当前正在聊天的对象，清空当前目标
      if (currentTargetId.value === userId) {
        chatStore.setTarget('', 'User')
      }
      
      // 重新加载最近聊天列表
      await updateRecentChatUsers()
      
      $q.notify({
        type: 'positive',
        message: '聊天记录已删除',
        position: 'top',
      })
    } catch (error: any) {
      console.error('删除聊天失败:', error)
      $q.notify({
        type: 'negative',
        message: error.message || '删除聊天失败',
        position: 'top',
      })
    }
  }

  // 处理查看群成员
  const handleViewGroupMembers = async (groupId: string) => {
    try {
      const { getGroupMembers } = await import('../services/im-group')
      const { getUser } = await import('../services/message')
      const members = await getGroupMembers(groupId)
      
      // 获取成员详细信息
      const memberDetails = await Promise.all(
        members.map(async (m) => {
          try {
            const userInfo = await getUser(m.member_id)
            return {
              ...m,
              name: userInfo?.name || m.alias || m.member_id,
              email: userInfo?.email || '',
            }
          } catch (e: any) {
            // 静默处理 404 错误（用户不存在）
            // 使用别名或 member_id 作为名称
            return {
              ...m,
              name: m.alias || m.member_id,
              email: '',
            }
          }
        })
      )
      
      // 显示群成员列表对话框
      $q.dialog({
        title: `群成员（共 ${members.length} 人）`,
        message: memberDetails.map((m, index) => 
          `${index + 1}. ${m.name}${m.alias ? ` (${m.alias})` : ''}${m.role === 1 ? ' [群主]' : m.role === 2 ? ' [管理员]' : ''}`
        ).join('\n'),
        html: false,
        persistent: true,
        style: 'max-width: 500px; max-height: 400px;',
      })
    } catch (error: any) {
      console.error('获取群成员失败:', error)
      $q.notify({
        type: 'negative',
        message: error.message || '获取群成员失败',
        position: 'top',
      })
    }
  }

  // 更新群成员缓存（用于快速查找群成员的alias和displayName）
  const updateGroupMembersCache = (groupId: string, members: Array<{ member_id: string; alias?: string; displayName?: string; name?: string }>) => {
    if (!groupId) return
    
    // 标准化groupId（去掉group_前缀，统一格式）
    const normalizedGroupId = groupId.replace(/^group_/i, '').trim().toLowerCase()
    
    // 清除该群组的旧缓存（支持多种格式）
    for (const key of groupMembersCache.value.keys()) {
      if (key.startsWith(`${normalizedGroupId}_`) || 
          key.startsWith(`${groupId}_`) ||
          key.startsWith(`group_${normalizedGroupId}_`)) {
        groupMembersCache.value.delete(key)
      }
    }
    
    // 添加新缓存（使用标准化后的groupId）
    for (const member of members) {
      const cacheKey = `${normalizedGroupId}_${member.member_id}`
      groupMembersCache.value.set(cacheKey, {
        alias: member.alias,
        displayName: member.displayName,
        name: member.name,
      })
    }
    
    // 触发响应式更新
    triggerRef(groupMembersCache)
  }
  
  // 更新单个群成员的alias（用于快速更新群昵称）
  const updateGroupMemberAlias = (groupId: string, memberId: string, alias: string | null) => {
    if (!groupId || !memberId) return
    
    // 标准化groupId（去掉group_前缀，统一格式）
    const normalizedGroupId = groupId.replace(/^group_/i, '').trim().toLowerCase()
    const cacheKey = `${normalizedGroupId}_${memberId}`
    
    const existingMember = groupMembersCache.value.get(cacheKey)
    if (existingMember) {
      // 更新缓存中的alias
      groupMembersCache.value.set(cacheKey, {
        ...existingMember,
        alias: alias || undefined,
      })
    } else {
      // 如果缓存中不存在，创建一个新条目
      groupMembersCache.value.set(cacheKey, {
        alias: alias || undefined,
        displayName: undefined,
        name: undefined,
      })
    }
    
    // 触发响应式更新
    triggerRef(groupMembersCache)
  }

  // 初始化 UI 模块（在函数定义之后）
  const uiModule = useChatWindowUI({
    $q,
    chatStore,
    currentTargetId,
    targetType,
    targetName,
    recentChatUsers,
    currentChatId,
    groupMemberCountCache,
    currentGroupMemberCount,
    groupMembersCache,
    updateRecentChatUsers,
    updateGroupMembersCache,
    loadGroupDrawerDetails: () => Promise.resolve(), // 临时占位，将在群组模块初始化后更新
    handleAddGroupMember: () => {}, // 临时占位，将在群组模块初始化后更新
  })

  // 初始化群组模块（在函数定义之后）
  const groupModule = useChatWindowGroup({
    $q,
    chatStore,
    currentTargetId,
    targetType,
    recentChatUsers,
    groupMemberCountCache,
    currentGroupMemberCount,
    groupMembersCache,
    updateRecentChatUsers,
    selectGroup,
    updateGroupMembersCache,
    detailsTargetId: uiModule.detailsTargetId,
    detailsTargetName: uiModule.detailsTargetName,
    showDetailsDrawer: uiModule.showDetailsDrawer,
    groupDrawerMembers: uiModule.groupDrawerMembers,
    currentGroupDrawerDetails: uiModule.currentGroupDrawerDetails,
    isGroupOwner: uiModule.isGroupOwner,
    groupChatRemark: uiModule.groupChatRemark,
    currentChatId,
    handleViewGroupMembers,
    showAddContactDialog: uiModule.showAddContactDialog,
    availableFriendsForAdd: uiModule.availableFriendsForAdd,
  })

  // 更新 UI 模块中的函数引用（在群组模块初始化后）
  if (uiModule.loadGroupDrawerDetails) {
    uiModule.loadGroupDrawerDetails.value = groupModule.loadGroupDrawerDetails
  }
  if (uiModule.handleAddGroupMember) {
    uiModule.handleAddGroupMember.value = groupModule.handleAddGroupMember
  }

  // 初始化好友模块（在函数定义之后）
  const friendModule = useChatWindowFriend({
    $q,
    chatStore,
    leftMenuActive,
    selectedFriend: ref<any>(null), // 好友模块会管理自己的 selectedFriend
    recentChatUsers,
    selectUser,
    loadUserList,
    handleVoiceCall: callModule.handleVoiceCall,
    handleVideoCall: callModule.handleVideoCall,
    callModule,
  })

  // 使用模块导出的函数（这些函数已经迁移到对应的模块中）
  const handleShowDetails = uiModule.handleShowDetails
  const handleShowDetailsFromHeader = uiModule.handleShowDetailsFromHeader
  const handleSearchChatHistory = uiModule.handleSearchChatHistory
  const handleClearChatHistory = uiModule.handleClearChatHistory
  const handleToggleDoNotDisturb = uiModule.handleToggleDoNotDisturb
  const handleTogglePinChat = uiModule.handleTogglePinChat
  const handleAddContact = uiModule.handleAddContact
  const handleAddContactConfirm = async (selectedFriendId: string) => {
    // 使用 UI 模块的函数，传入群组模块的 loadGroupDrawerDetails
    await uiModule.handleAddContactConfirm(selectedFriendId, groupModule.loadGroupDrawerDetails)
  }

  // 好友模块函数
  const handleShowFriendDetails = friendModule.handleShowFriendDetails
  const handleSelectFriendFromList = friendModule.handleSelectFriendFromList
  const handleShowFriendDetailsFromList = friendModule.handleShowFriendDetailsFromList
  const handleSendMessageToFriend = friendModule.handleSendMessageToFriend
  const handleUpdateFriendRemark = friendModule.handleUpdateFriendRemark
  const handleVoiceCallFromFriend = friendModule.handleVoiceCallFromFriend
  const handleVideoCallFromFriend = friendModule.handleVideoCallFromFriend
  const handleAddFriendFromGroup = friendModule.handleAddFriendFromGroup

  // 群组模块函数
  const handleShowGroupDetailsFromList = groupModule.handleShowGroupDetailsFromList
  const loadGroupDrawerDetails = groupModule.loadGroupDrawerDetails
  const handleAddGroupMember = groupModule.handleAddGroupMember
  const handleViewGroupAnnouncement = groupModule.handleViewGroupAnnouncement
  const handleRemoveGroupMember = groupModule.handleRemoveGroupMember
  const handleShowRemoveMemberDialog = groupModule.handleShowRemoveMemberDialog
  const handleUpdateGroupName = groupModule.handleUpdateGroupName
  const handleUpdateGroupRemark = groupModule.handleUpdateGroupRemark
  const handleDissolveGroup = groupModule.handleDissolveGroup
  const handleLeaveGroup = groupModule.handleLeaveGroup
  const handleCreateGroup = groupModule.handleCreateGroup
  const doCreateGroup = groupModule.doCreateGroup

  return {
    // 状态
    messagesContainerRef,
    fileInputRef,
    inputContentRef,
    inputContent,
    sending,
    showUserDialog,
    showEmojiPicker,
    newFriendForm,
    userList,
    friendList,
    recentChatUsers,
    selectedFiles,
    uploading,
    searchingFriend,
    searchQuery,
    leftMenuActive,
    showSettings,
    currentUser,
    // 计算属性
    currentTargetId,
    currentMessages,
    connectionStatus,
    isConnected,
    targetName,
    middleContent,
    filteredMiddleContent,
    // 方法
    formatTime,
    getLastMessagePreview,
    getLastMessageTime,
    getUserInitial,
    getUserName,
    scrollToBottom,
    loadFriends,
    updateRecentChatUsers,
    loadUserList,
    selectUser,
    selectGroup,
    handleSend,
    handleEmojiSelect,
    handleEnterKey,
    handleShiftEnter,
    handleFileSelect,
    handleRemoveFile,
    handleAddFriend,
    handleLogout,
    handleVoiceCall,
    handleVideoCall,
    handleCallEnded,
    showVideoCall: callModule.showVideoCall,
    isVoiceCall: callModule.isVoiceCall,
    isGroupCall: callModule.isGroupCall,
    videoCallTargetUserId: callModule.videoCallTargetUserId,
    videoCallTargetName: callModule.videoCallTargetName,
    callInviteData: callModule.callInviteData,
    showGroupCallDialog: callModule.showGroupCallDialog,
    groupCallMembers: callModule.groupCallMembers,
    handleGroupVoiceCall,
    handleGroupVideoCall,
    handleShowGroupCallDialog: callModule.handleShowGroupCallDialog,
    handleCallInviteAccept,
    handleCallInviteReject,
    handleCallInviteTimeout,
    handleUserUpdated,
    handleDeleteChat,
    handleViewGroupMembers,
    handleAddFriendFromGroup,
    updateGroupMembersCache,
    updateGroupMemberAlias,
    init,
    cleanup,
    // 计算属性（从模块中获取）
    currentChatId,
    currentChatType,
    currentChatSequence,
    currentReadSequence,
    currentMemberCount,
    // 群组人数缓存
    groupMemberCountCache,
    GROUP_MEMBER_COUNT_CACHE_DURATION,
    currentGroupMemberCount,
    // 好友模块状态和函数
    selectedFriend: friendModule.selectedFriend,
    selectedFriendId: friendModule.selectedFriendId,
    handleShowFriendDetails,
    handleSelectFriendFromList,
    handleShowFriendDetailsFromList,
    handleSendMessageToFriend,
    handleUpdateFriendRemark,
    handleVoiceCallFromFriend,
    handleVideoCallFromFriend,
    // UI 模块状态和函数
    showDetailsDrawer: uiModule.showDetailsDrawer,
    detailsDrawerType: uiModule.detailsDrawerType,
    detailsTargetId: uiModule.detailsTargetId,
    detailsTargetName: uiModule.detailsTargetName,
    doNotDisturb: uiModule.doNotDisturb,
    pinChat: uiModule.pinChat,
    groupMemberSearch: uiModule.groupMemberSearch,
    groupDrawerMembers: uiModule.groupDrawerMembers,
    currentGroupDrawerDetails: uiModule.currentGroupDrawerDetails,
    showAllMembers: uiModule.showAllMembers,
    isGroupOwner: uiModule.isGroupOwner,
    groupChatRemark: uiModule.groupChatRemark,
    showUserMenu: uiModule.showUserMenu,
    showHelp: uiModule.showHelp,
    showAddContactDialog: uiModule.showAddContactDialog,
    availableFriendsForAdd: uiModule.availableFriendsForAdd,
    handleShowDetails,
    handleShowDetailsFromHeader,
    handleSearchChatHistory,
    handleClearChatHistory,
    handleAddContact,
    handleAddContactConfirm,
    handleToggleDoNotDisturb,
    handleTogglePinChat,
    // 群组模块状态和函数
    showCreateGroupDialog: groupModule.showCreateGroupDialog,
    creatingGroup: groupModule.creatingGroup,
    handleShowGroupDetailsFromList,
    loadGroupDrawerDetails,
    handleAddGroupMember,
    handleViewGroupAnnouncement,
    handleRemoveGroupMember,
    handleShowRemoveMemberDialog,
    handleUpdateGroupName,
    handleUpdateGroupRemark,
    handleDissolveGroup,
    handleLeaveGroup,
    handleCreateGroup,
    doCreateGroup,
  }
}
